libmicrohttpd

HTTP/1.x server C library (MHD 1.x, stable)
Log | Files | Refs | Submodules | README | LICENSE

commit bf47e27d79bc41f979af0f4206f669f31cf3d031
parent fe4e71ecaa788e73582b7ee29d1835a3e3112be3
Author: Christian Grothoff <christian@grothoff.org>
Date:   Sat, 25 Dec 2010 21:57:23 +0000

removing client authentication API, moving it into tutorial

Diffstat:
MChangeLog | 3++-
MREADME | 1+
Mdoc/chapters/tlsauthentication.inc | 172++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++-
Mdoc/microhttpd-tutorial.texi | 8++++----
Mdoc/microhttpd.texi | 56+++++++++-----------------------------------------------
Msrc/daemon/EXPORT.sym | 4+---
Msrc/daemon/connection.c | 5+----
Msrc/daemon/daemon.c | 2--
Msrc/daemon/digestauth.c | 263+++++++++++++++++++++++--------------------------------------------------------
Msrc/daemon/internal.h | 10----------
Msrc/include/microhttpd.h | 41+++--------------------------------------
11 files changed, 267 insertions(+), 298 deletions(-)

diff --git a/ChangeLog b/ChangeLog @@ -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 diff --git a/README b/README @@ -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": diff --git a/doc/chapters/tlsauthentication.inc b/doc/chapters/tlsauthentication.inc @@ -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 + diff --git a/doc/microhttpd-tutorial.texi b/doc/microhttpd-tutorial.texi @@ -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 diff --git a/doc/microhttpd.texi b/doc/microhttpd.texi @@ -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) @@ -1645,39 +1640,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 @cindex POST method diff --git a/src/daemon/EXPORT.sym b/src/daemon/EXPORT.sym @@ -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 + diff --git a/src/daemon/connection.c b/src/daemon/connection.c @@ -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; diff --git a/src/daemon/daemon.c b/src/daemon/daemon.c @@ -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) diff --git a/src/daemon/digestauth.c b/src/daemon/digestauth.c @@ -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,208 +654,94 @@ 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) { -#if HAVE_MESSAGES - MHD_DLOG(connection->daemon, "Error decoding basic authentication\n"); -#endif - 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"); -#endif - free(decode); - return NULL; - } - user = strndup(decode, separator - decode); - if (password != NULL) { - *password = strdup(separator + 1); - } - free(decode); - return user; -} - -/** - * Queues a response to request basic authentication from the client - * - * @param connection The MHD connection structure - * @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; -} - -#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) { + 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, - "Failed to retrieve client certificate chain\n"); + MHD_DLOG(connection->daemon, + "Error decoding basic authentication\n"); #endif - connection->client_cert_status = GNUTLS_CERT_INVALID; - return NULL; - } - - if (gnutls_x509_crt_init(&connection->client_cert)) { + return NULL; + } + /* Find user:password pattern */ + separator = strstr(decode, ":"); + if (separator == NULL) + { #if HAVE_MESSAGES - MHD_DLOG(connection->daemon, - "Failed to initialize client certificate\n"); + MHD_DLOG(connection->daemon, + "Basic authentication doesn't contain ':' separator\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)) { + 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 import client certificate\n"); + MHD_DLOG(connection->daemon, + "Failed to allocate memory for password\n"); #endif - gnutls_x509_crt_deinit(connection->client_cert); - connection->client_cert = NULL; - connection->client_cert_status = GNUTLS_CERT_INVALID; - return NULL; - } + free (decode); + free (user); + return NULL; } - - return connection->client_cert; + } + free(decode); + return user; } -/** - * 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 + * Queues a response to request basic authentication from the client * * @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 + * @param realm the realm presented to the client + * @return MHD_YES on success, MHD_NO otherwise */ -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; - - } - +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; } -#endif /* HTTPS */ - /* end of digestauth.c */ diff --git a/src/daemon/internal.h b/src/daemon/internal.h @@ -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 }; diff --git a/src/include/microhttpd.h b/src/include/microhttpd.h @@ -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 ********************** */