removing client authentication API, moving it into tutorial

This commit is contained in:
Christian Grothoff
2010-12-25 21:57:23 +00:00
parent fe4e71ecaa
commit bf47e27d79
11 changed files with 267 additions and 298 deletions
+2 -1
View File
@@ -1,5 +1,6 @@
Sat Dec 25 21:57:14 CET 2010
Adding support for basic authentication and client SSL certificates. -MS
Adding support for basic authentication.
Documented how to obtain client SSL certificates in tutorial. -MS
Thu Dec 23 15:40:36 CET 2010
Increasing nonce length to 128 to support digest authentication
+1
View File
@@ -92,6 +92,7 @@ Untested features:
- extend testcase for chunked encoding to validate
handling of footers
- more testing for SSL support
- MHD basic and digest authentication
Functions not covered by "make check":
+171 -1
View File
@@ -105,6 +105,7 @@ The rest consists of little new besides some additional memory cleanups.
The rather unexciting file loader can be found in the complete example @code{tlsauthentication.c}.
@heading Remarks
@itemize @bullet
@item
@@ -126,5 +127,174 @@ hardcode certificates in embedded devices.
The cryptographic facilities consume memory space and computing time. For this reason, websites usually consists
both of uncritically @emph{HTTP} parts and secured @emph{HTTPS}.
@end itemize
@heading Client authentication
You can also use MHD to authenticate the client via SSL/TLS certificates
(as an alternative to using the password-based Basic or Digest authentication).
To do this, you will need to link your application against @emph{gnutls}.
For this, you first need to obtain the raw GnuTLS session handle from
@emph{MHD} using @code{MHD_get_connection_info}.
@verbatim
#include <gnutls/gnutls.h>
#include <gnutls/x509.h>
gnutls_session_t tls_session;
tls_session = MHD_get_connection_info (connection,
MHD_CONNECTION_INFO_GNUTLS_SESSION);
@end verbatim
You can then extract the client certificate:
@verbatim
/**
* Get the client's certificate
*
* @param tls_session the TLS session
* @return NULL if no valid client certificate could be found, a pointer
* to the certificate if found
*/
static gnutls_x509_crt_t
get_client_certificate (gnutls_session_t tls_session)
{
unsigned int listsize;
const gnutls_datum_t * pcert;
gnutls_certificate_status_t client_cert_status;
gnutls_x509_crt_t client_cert;
if (tls_session == NULL)
return NULL;
if (gnutls_certificate_verify_peers2(tls_session,
&client_cert_status))
return NULL;
pcert = gnutls_certificate_get_peers(tls_session,
&listsize);
if ( (pcert == NULL) ||
(listsize == 0))
{
fprintf (stderr,
"Failed to retrieve client certificate chain\n");
return NULL;
}
if (gnutls_x509_crt_init(&client_cert))
{
fprintf (stderr,
"Failed to initialize client certificate\n");
return NULL;
}
/* Note that by passing values between 0 and listsize here, you
can get access to the CA's certs */
if (gnutls_x509_crt_import(client_cert,
&pcert[0],
GNUTLS_X509_FMT_DER))
{
fprintf (stderr,
"Failed to import client certificate\n");
gnutls_x509_crt_deinit(client_cert);
return NULL;
}
return client_cert;
}
@end verbatim
Using the client certificate, you can then get the client's distinguished name
and alternative names:
@verbatim
/**
* Get the distinguished name from the client's certificate
*
* @param client_cert the client certificate
* @return NULL if no dn or certificate could be found, a pointer
* to the dn if found
*/
char *
cert_auth_get_dn(gnutls_x509_crt_c client_cert)
{
char* buf;
size_t lbuf;
lbuf = 0;
gnutls_x509_crt_get_dn(client_cert, NULL, &lbuf);
buf = malloc(lbuf);
if (buf == NULL)
{
fprintf (stderr,
"Failed to allocate memory for certificate dn\n");
return NULL;
}
gnutls_x509_crt_get_dn(client_cert, buf, &lbuf);
return buf;
}
/**
* Get the alternative name of specified type from the client's certificate
*
* @param client_cert the client certificate
* @param nametype The requested name type
* @param index The position of the alternative name if multiple names are
* matching the requested type, 0 for the first matching name
* @return NULL if no matching alternative name could be found, a pointer
* to the alternative name if found
*/
char *
MHD_cert_auth_get_alt_name(gnutls_x509_crt_t client_cert,
int nametype,
unsigned int index)
{
char* buf;
size_t lbuf;
unsigned int seq;
unsigned int subseq;
unsigned int type;
int result;
subseq = 0;
for (seq=0;;seq++)
{
lbuf = 0;
result = gnutls_x509_crt_get_subject_alt_name2(client_cert, seq, NULL, &lbuf,
&type, NULL);
if (result == GNUTLS_E_REQUESTED_DATA_NOT_AVAILABLE)
return NULL;
if (nametype != (int) type)
continue;
if (subseq == index)
break;
subseq++;
}
buf = malloc(lbuf);
if (buf == NULL)
{
fprintf (stderr,
"Failed to allocate memory for certificate alt name\n");
return NULL;
}
result = gnutls_x509_crt_get_subject_alt_name2(client_cert,
seq,
buf,
&lbuf,
NULL, NULL);
if (result != nametype)
{
fprintf (stderr,
"Unexpected return value from gnutls: %d\n",
result);
free (buf);
return NULL;
}
return buf;
}
@end verbatim
Finally, you should release the memory associated with the client
certificate:
@verbatim
gnutls_x509_crt_deinit (client_cert);
@end verbatim
+4 -4
View File
@@ -1,10 +1,10 @@
\input texinfo @c -*-texinfo-*-
@finalout
@setfilename microhttpd-tutorial.info
@set UPDATED 26 Jul 2010
@set UPDATED-MONTH Jul 2010
@set EDITION 0.9.0
@set VERSION 0.9.0
@set UPDATED 26 Dec 2010
@set UPDATED-MONTH Dec 2010
@set EDITION 0.9.3
@set VERSION 0.9.3
@settitle A tutorial for GNU libmicrohttpd
@dircategory GNU Libraries
+9 -47
View File
@@ -690,14 +690,9 @@ Takes no extra arguments. Allows finding out the TLS/SSL protocol used
(HTTPS connections only).
@item MHD_CONNECTION_INFO_GNUTLS_SESSION,
Takes no extra arguments. Allows access to the underlying GNUtls session
(HTTPS connections only).
@item MHD_CONNECTION_INFO_GNUTLS_CLIENT_CERT
Allows access to the underlying GNUtls client certificate.
Equivalent to calling directly MHD_cert_auth_get_certificate.
Takes no extra arguments.
(HTTPS connections only).
Takes no extra arguments. Allows access to the underlying GNUtls session,
including access to the underlying GNUtls client certificate
(HTTPS connections only). Takes no extra arguments.
@end table
@end deftp
@@ -1497,15 +1492,15 @@ when https is not used to encrypt the session.
Client certificate authentication uses a X.509 certificate from
the client. This is the strongest authentication mechanism but it
requires the use of https. Client certificate authentication can
requires the use of HTTPS. Client certificate authentication can
be used simultaneously with Basic or Digest Authentication in order
to provide a two levels authentication (like for instance separate
machine and user authentication).
machine and user authentication). A code example for using
client certificates is presented in the @mhd{} tutorial.
@menu
* microhttpd-dauth basic:: Using Basic Authentication.
* microhttpd-dauth digest:: Using Digest Authentication.
* microhttpd-dauth cert:: Using Client Certificate Authentication.
@end menu
@c ++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++
@@ -1517,10 +1512,10 @@ machine and user authentication).
@deftypefun {char *} MHD_basic_auth_get_username_password (struct MHD_Connection *connection, char** password)
Get the username and password from the basic authorization header sent by the client.
Return @mynull{} if no username could be found, a pointer to the username if found.
If returned value is not @mynull{}, the value must be free()'ed.
If returned value is not @mynull{}, the value must be @code{free()}'ed.
@var{password} reference a buffer to store the password. It can be @mynull{}.
If returned value is not @mynull{}, the value must be free()'ed.
If returned value is not @mynull{}, the value must be @code{free()}'ed.
@end deftypefun
@deftypefun {int} MHD_queue_basic_auth_fail_response (struct MHD_Connection *connection, const char *realm, struct MHD_Response *response)
@@ -1542,7 +1537,7 @@ client with a 401 HTTP status.
@deftypefun {char *} MHD_digest_auth_get_username (struct MHD_Connection *connection)
Find and return a pointer to the username value from the request header.
Return @mynull{} if the value is not found or header does not exist.
If returned value is not @mynull{}, the value must be free()'ed.
If returned value is not @mynull{}, the value must be @code{free()}'ed.
@end deftypefun
@deftypefun int MHD_digest_auth_check (struct MHD_Connection *connection, const char *realm, const char *username, const char *password, unsigned int nonce_timeout)
@@ -1644,39 +1639,6 @@ ahc_echo (void *cls,
@c ++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++
@c ------------------------------------------------------------
@node microhttpd-dauth cert
@section Using Client Certificate Authentication
@deftypefun {void *} MHD_cert_auth_get_certificate (struct MHD_Connection *connection)
Get the client's X.509 certificate.
Return @mynull{} if no valid client certificate was found, a pointer to the certificate
(which can be casted to gnutls_x509_crt_t) if found.
The certificate is cached between calls for a same https session and must not but
manually modified or free()'ed.
@end deftypefun
@deftypefun {char *} MHD_cert_auth_get_dn (struct MHD_Connection *connection)
Get the distinguished name from the client's certificate.
Return @mynull{} if the certificate doesn't contain a dn or if no valid certificate was
found, a pointer to the dn if found. If returned value is not @mynull{}, the value must
be free()'ed.
@end deftypefun
@deftypefun {char *} MHD_cert_auth_get_alt_name (struct MHD_Connection *connection, int nametype, unsigned int index)
Get the alternative name of specified type from the client's certificate.
Return @mynull{} if the certificate doesn't contain a matching alternative name or if no
valid certificate was found, a pointer to the alternative name if found. If returned
value is not @mynull{}, the value must be free()'ed.
@var{nametype} The requested name type (of type 'enum gnutls_x509_subject_alt_name_t')
@var{index} The position of the alternative name if multiple names are matching the
requested type, 0 for the first matching name
@end deftypefun
@c ++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++
@c ------------------------------------------------------------
@node microhttpd-post
@chapter Adding a @code{POST} processor
+1 -3
View File
@@ -29,6 +29,4 @@ MHD_digest_auth_check
MHD_queue_auth_fail_response
MHD_basic_auth_get_username_password
MHD_queue_basic_auth_fail_response
MHD_cert_auth_get_certificate
MHD_cert_auth_get_dn
MHD_cert_auth_get_alt_name
+1 -4
View File
@@ -2277,6 +2277,7 @@ MHD_connection_handle_idle (struct MHD_Connection *connection)
return MHD_YES;
}
void
MHD_set_http_callbacks_ (struct MHD_Connection *connection)
{
@@ -2316,10 +2317,6 @@ MHD_get_connection_info (struct MHD_Connection *connection,
if (connection->tls_session == NULL)
return NULL;
return (const union MHD_ConnectionInfo *) &connection->tls_session;
#if DAUTH_SUPPORT
case MHD_CONNECTION_INFO_GNUTLS_CLIENT_CERT:
return (const union MHD_ConnectionInfo *) MHD_cert_auth_get_certificate(connection);
#endif
#endif
case MHD_CONNECTION_INFO_CLIENT_ADDRESS:
return (const union MHD_ConnectionInfo *) &connection->addr;
-2
View File
@@ -1075,8 +1075,6 @@ MHD_cleanup_connections (struct MHD_Daemon *daemon)
#if HTTPS_SUPPORT
if (pos->tls_session != NULL)
gnutls_deinit (pos->tls_session);
if (pos->client_cert != NULL)
gnutls_x509_crt_deinit (pos->client_cert);
#endif
MHD_ip_limit_del (daemon, (struct sockaddr*)pos->addr, pos->addr_len);
if (pos->response != NULL)
+75 -188
View File
@@ -643,6 +643,7 @@ MHD_queue_auth_fail_response(struct MHD_Connection *connection,
return ret;
}
/**
* Get the username and password from the basic authorization header sent by the client
*
@@ -653,45 +654,61 @@ MHD_queue_auth_fail_response(struct MHD_Connection *connection,
*/
char *
MHD_basic_auth_get_username_password(struct MHD_Connection *connection,
char** password) {
size_t len;
const char *header;
char *decode;
const char *separator;
char *user;
header = MHD_lookup_connection_value(connection, MHD_HEADER_KIND,
MHD_HTTP_HEADER_AUTHORIZATION);
if (header == NULL)
return NULL;
if (strncmp(header, _BASIC_BASE, strlen(_BASIC_BASE)) != 0)
return NULL;
header += strlen(_BASIC_BASE);
decode = BASE64Decode(header);
if (decode == NULL) {
char** password)
{
const char *header;
char *decode;
const char *separator;
char *user;
header = MHD_lookup_connection_value(connection,
MHD_HEADER_KIND,
MHD_HTTP_HEADER_AUTHORIZATION);
if (header == NULL)
return NULL;
if (strncmp(header, _BASIC_BASE, strlen(_BASIC_BASE)) != 0)
return NULL;
header += strlen(_BASIC_BASE);
decode = BASE64Decode(header);
if (decode == NULL)
{
#if HAVE_MESSAGES
MHD_DLOG(connection->daemon, "Error decoding basic authentication\n");
MHD_DLOG(connection->daemon,
"Error decoding basic authentication\n");
#endif
return NULL;
}
/* Find user:password pattern */
separator = strstr(decode, ":");
if (separator == NULL) {
return NULL;
}
/* Find user:password pattern */
separator = strstr(decode, ":");
if (separator == NULL)
{
#if HAVE_MESSAGES
MHD_DLOG(connection->daemon,
"Basic authentication doesn't contain ':' separator\n");
MHD_DLOG(connection->daemon,
"Basic authentication doesn't contain ':' separator\n");
#endif
free(decode);
return NULL;
free(decode);
return NULL;
}
user = strndup(decode, separator - decode);
if (password != NULL)
{
*password = strdup(separator + 1);
if (NULL == *password)
{
#if HAVE_MESSAGES
MHD_DLOG(connection->daemon,
"Failed to allocate memory for password\n");
#endif
free (decode);
free (user);
return NULL;
}
user = strndup(decode, separator - decode);
if (password != NULL) {
*password = strdup(separator + 1);
}
free(decode);
return user;
}
free(decode);
return user;
}
/**
* Queues a response to request basic authentication from the client
*
@@ -699,162 +716,32 @@ MHD_basic_auth_get_username_password(struct MHD_Connection *connection,
* @param realm the realm presented to the client
* @return MHD_YES on success, MHD_NO otherwise
*/
int MHD_queue_basic_auth_fail_response(struct MHD_Connection *connection,
const char *realm, struct MHD_Response *response) {
int ret;
size_t hlen = strlen(realm) + sizeof("Basic realm=\"\"");
char header[hlen];
snprintf(header, sizeof(header), "Basic realm=\"%s\"", realm);
ret = MHD_add_response_header(response, MHD_HTTP_HEADER_WWW_AUTHENTICATE,
header);
if (MHD_YES == ret)
ret = MHD_queue_response(connection, MHD_HTTP_UNAUTHORIZED, response);
return ret;
int
MHD_queue_basic_auth_fail_response(struct MHD_Connection *connection,
const char *realm,
struct MHD_Response *response)
{
int ret;
size_t hlen = strlen(realm) + strlen("Basic realm=\"\"") + 1;
char header[hlen];
if (hlen !=
snprintf(header,
sizeof(header),
"Basic realm=\"%s\"",
realm))
{
EXTRA_CHECK (0);
return MHD_NO;
}
ret = MHD_add_response_header(response,
MHD_HTTP_HEADER_WWW_AUTHENTICATE,
header);
if (MHD_YES == ret)
ret = MHD_queue_response(connection,
MHD_HTTP_UNAUTHORIZED,
response);
return ret;
}
#if HTTPS_SUPPORT
/**
* Get the client's certificate
*
* @param connection The MHD connection structure
* @return NULL if no valid client certificate could be found, a pointer
* to the certificate if found
*/
void*
MHD_cert_auth_get_certificate(struct MHD_Connection *connection) {
if (connection->client_cert == NULL && connection->client_cert_status == 0) {
if (connection->tls_session == NULL) {
connection->client_cert_status = GNUTLS_CERT_INVALID;
return NULL;
}
if (gnutls_certificate_verify_peers2(connection->tls_session,
&connection->client_cert_status)) {
connection->client_cert_status = GNUTLS_CERT_INVALID;
return NULL;
}
unsigned int listsize;
const gnutls_datum_t * pcert = gnutls_certificate_get_peers(
connection->tls_session, &listsize);
if (pcert == NULL || listsize == 0) {
#if HAVE_MESSAGES
MHD_DLOG(connection->daemon,
"Failed to retrieve client certificate chain\n");
#endif
connection->client_cert_status = GNUTLS_CERT_INVALID;
return NULL;
}
if (gnutls_x509_crt_init(&connection->client_cert)) {
#if HAVE_MESSAGES
MHD_DLOG(connection->daemon,
"Failed to initialize client certificate\n");
#endif
connection->client_cert = NULL;
connection->client_cert_status = GNUTLS_CERT_INVALID;
return NULL;
}
if (gnutls_x509_crt_import(connection->client_cert, &pcert[0],
GNUTLS_X509_FMT_DER)) {
#if HAVE_MESSAGES
MHD_DLOG(connection->daemon,
"Failed to import client certificate\n");
#endif
gnutls_x509_crt_deinit(connection->client_cert);
connection->client_cert = NULL;
connection->client_cert_status = GNUTLS_CERT_INVALID;
return NULL;
}
}
return connection->client_cert;
}
/**
* Get the distinguished name from the client's certificate
*
* @param connection The MHD connection structure
* @return NULL if no dn or certificate could be found, a pointer
* to the dn if found
*/
char *
MHD_cert_auth_get_dn(struct MHD_Connection *connection) {
char* buf;
size_t lbuf = 0;
gnutls_x509_crt_t cert = MHD_cert_auth_get_certificate(connection);
if (cert == NULL)
return NULL;
gnutls_x509_crt_get_dn(cert, NULL, &lbuf);
buf = malloc(lbuf);
if (buf == NULL) {
#if HAVE_MESSAGES
MHD_DLOG(connection->daemon,
"Failed to allocate memory for certificate dn\n");
#endif
return NULL;
}
gnutls_x509_crt_get_dn(cert, buf, &lbuf);
return buf;
}
/**
* Get the alternative name of specified type from the client's certificate
*
* @param connection The MHD connection structure
* @param nametype The requested name type
* @param index The position of the alternative name if multiple names are
* matching the requested type, 0 for the first matching name
* @return NULL if no matching alternative name could be found, a pointer
* to the alternative name if found
*/
char *
MHD_cert_auth_get_alt_name(struct MHD_Connection *connection, int nametype, unsigned int index) {
char* buf;
size_t lbuf;
unsigned int seq = 0;
unsigned int subseq = 0;
int result;
unsigned int type;
gnutls_x509_crt_t cert = MHD_cert_auth_get_certificate(connection);
if (cert == NULL)
return NULL;
for (;; seq++) {
lbuf = 0;
result = gnutls_x509_crt_get_subject_alt_name2(cert, seq, NULL, &lbuf,
&type, NULL);
if (result == GNUTLS_E_REQUESTED_DATA_NOT_AVAILABLE)
return NULL;
if (type != nametype)
continue;
if (subseq != index) {
subseq++;
continue;
}
buf = malloc(lbuf);
if (buf == NULL) {
#if HAVE_MESSAGES
MHD_DLOG(connection->daemon,
"Failed to allocate memory for certificate alt name\n");
#endif
return NULL;
}
gnutls_x509_crt_get_subject_alt_name2(cert, seq, buf, &lbuf, NULL, NULL);
return buf;
}
}
#endif /* HTTPS */
/* end of digestauth.c */
-10
View File
@@ -706,16 +706,6 @@ struct MHD_Connection
*/
int cipher;
/**
* Validation status of client's certificate.
*/
gnutls_certificate_status_t client_cert_status;
/**
* Client's certificate.
*/
gnutls_x509_crt_t client_cert;
#endif
};
+3 -38
View File
@@ -1450,7 +1450,7 @@ MHD_queue_auth_fail_response (struct MHD_Connection *connection,
*/
char *
MHD_basic_auth_get_username_password(struct MHD_Connection *connection,
char** password);
char** password);
/**
* Queues a response to request basic authentication from the client
@@ -1461,43 +1461,8 @@ MHD_basic_auth_get_username_password(struct MHD_Connection *connection,
*/
int
MHD_queue_basic_auth_fail_response(struct MHD_Connection *connection,
const char *realm,
struct MHD_Response *response);
/**
* Get the client's certificate
*
* @param connection The MHD connection structure
* @return NULL if no valid client certificate could be found, a pointer
* to the certificate if found
*/
void*
MHD_cert_auth_get_certificate(struct MHD_Connection *connection);
/**
* Get the distinguished name from the client's certificate
*
* @param connection The MHD connection structure
* @return NULL if no dn or certificate could be found, a pointer
* to the dn if found
*/
char *
MHD_cert_auth_get_dn(struct MHD_Connection *connection);
/**
* Get the alternative name of specified type from the client's certificate
*
* @param connection The MHD connection structure
* @param nametype The requested name type
* @param index The position of the alternative name if multiple names are
* matching the requested type, 0 for the first matching name
* @return NULL if no matching alternative name could be found, a pointer
* to the alternative name if found
*/
char *
MHD_cert_auth_get_alt_name(struct MHD_Connection *connection,
int nametype,
unsigned int index);
const char *realm,
struct MHD_Response *response);
/* ********************** generic query functions ********************** */