gnunet-svn
[Top][All Lists]
Advanced

[Date Prev][Date Next][Thread Prev][Thread Next][Date Index][Thread Index]

[GNUnet-SVN] r12709 - in libmicrohttpd: . src/daemon src/examples src/in


From: gnunet
Subject: [GNUnet-SVN] r12709 - in libmicrohttpd: . src/daemon src/examples src/include
Date: Sun, 22 Aug 2010 16:49:49 +0200

Author: grothoff
Date: 2010-08-22 16:49:48 +0200 (Sun, 22 Aug 2010)
New Revision: 12709

Added:
   libmicrohttpd/src/daemon/digestauth.c
   libmicrohttpd/src/examples/digest_auth_example.c
Modified:
   libmicrohttpd/AUTHORS
   libmicrohttpd/ChangeLog
   libmicrohttpd/configure.ac
   libmicrohttpd/src/daemon/Makefile.am
   libmicrohttpd/src/examples/Makefile.am
   libmicrohttpd/src/include/microhttpd.h
Log:
initial draft for digest authentication (based on patch R3)

Modified: libmicrohttpd/AUTHORS
===================================================================
--- libmicrohttpd/AUTHORS       2010-08-22 13:36:42 UTC (rev 12708)
+++ libmicrohttpd/AUTHORS       2010-08-22 14:49:48 UTC (rev 12709)
@@ -3,6 +3,7 @@
 Nils Durner <address@hidden> (W32 port)
 Sagie Amir (TLS/SSL support using GNUtls)
 Richard Alimi <address@hidden> (performance)
+Amr Ali <address@hidden> (digest authentication)
 
 Code contributions also came from:
 Chris GauthierDickey <address@hidden>

Modified: libmicrohttpd/ChangeLog
===================================================================
--- libmicrohttpd/ChangeLog     2010-08-22 13:36:42 UTC (rev 12708)
+++ libmicrohttpd/ChangeLog     2010-08-22 14:49:48 UTC (rev 12709)
@@ -1,3 +1,6 @@
+Sun Aug 22 16:49:13 CEST 2010
+       Initial draft for digest authentication. -AA
+
 Thu Aug 19 14:15:01 CEST 2010
        Changed code to enable error messages and HTTPS by default;
        added option to disable post processor API (use

Modified: libmicrohttpd/configure.ac
===================================================================
--- libmicrohttpd/configure.ac  2010-08-22 13:36:42 UTC (rev 12708)
+++ libmicrohttpd/configure.ac  2010-08-22 14:49:48 UTC (rev 12709)
@@ -221,31 +221,37 @@
 
 # optional: have error messages ?
 AC_MSG_CHECKING(whether to enable error messages)
-AC_ARG_ENABLE(messages,
+AC_ARG_ENABLE([messages],
    [AS_HELP_STRING([--disable-messages],
-               [disable MHD error messages])],
-   [disable_messages=$enableval],
+               [disable MHD error messages (default is NO)])],
+   [disable_messages="yes"],
    [disable_messages="no"])
 AC_MSG_RESULT($disable_messages)
 if test "$disable_messages" = "no"
 then
  AC_DEFINE([HAVE_MESSAGES],[1],[Include error messages])
+ enable_messages="yes"
+else
+ AC_DEFINE([HAVE_MESSAGES],[0],[Disable error messages])
+ enable_messages="no"
 fi
 
 
 # optional: have postprocessor?
 AC_MSG_CHECKING(whether to enable postprocessor)
-AC_ARG_ENABLE(postprocessor,
+AC_ARG_ENABLE([postprocessor],
    [AS_HELP_STRING([--disable-postprocessor],
-               [disable MHD PostProcessor functionality])],
-   [disable_postprocessor=$enableval],
+               [disable MHD PostProcessor functionality (default is NO)])],
+   [disable_postprocessor="yes"],
    [disable_postprocessor="no"])
 AC_MSG_RESULT($disable_postprocessor)
 if test "$disable_postprocessor" = "no"
 then
- AC_DEFINE([HAVE_POSTPROCESSOR],[1],[Include postprocessor])
+ enable_postprocessor="yes"
+else
+ enable_postprocessor="no"
 fi
-AM_CONDITIONAL([HAVE_POSTPROCESSOR],test x$disable_postprocessor = xno)
+AM_CONDITIONAL([HAVE_POSTPROCESSOR],test x$disable_postprocessor != xyes)
 
 
 # optional: have zzuf, socat?
@@ -300,8 +306,8 @@
 AC_MSG_CHECKING(whether to enable HTTPS support)
 AC_ARG_ENABLE([https],
    [AS_HELP_STRING([--disable-https],
-               [disable HTTPS support (default is enabled)])],
-   [disable_https=$enableval],
+               [disable HTTPS support (default is NO)])],
+   [disable_https="yes"],
    [disable_https="no"])
 if test "$disable_https" = "no"
 then
@@ -309,17 +315,44 @@
  then
    AC_DEFINE([HTTPS_SUPPORT],[1],[include HTTPS support])
    MHD_LIBDEPS="$LIBGCRYPT_LIBS"
+   enable_https="yes"
  else
    AC_DEFINE([HTTPS_SUPPORT],[0],[no libgcrypt or libgnutls])
    enable_https="no (lacking libgcrypt or libgnutls)"
  fi
 else
  AC_DEFINE([HTTPS_SUPPORT],[0],[disable HTTPS support])
+  enable_https="no (disabled)"
 fi
 AC_MSG_RESULT($disable_https)
 
 AM_CONDITIONAL(ENABLE_HTTPS, test "$disable_https" = "no")
 
+# optional: HTTP Digest Auth support. Enabled by default
+AC_MSG_CHECKING(whether to disable HTTP Digest Auth support)
+AC_ARG_ENABLE([dauth],
+               AS_HELP_STRING([--disable-dauth],
+                       [disable HTTP Digest Auth support (default is NO)]),
+               [disable_dauth="yes"],
+               [disable_dauth="no"])
+
+if test "x$disable_dauth" != "xyes"
+then
+ if test "$gcrypt" = "true"
+ then
+   enable_dauth="yes"
+   MHD_LIBDEPS="$LIBGCRYPT_LIBS"
+ else
+   disable_dauth="yes (lacking libgcrypt)"
+   enable_dauth="no (lacking libgcrypt)"
+ fi
+else
+ enable_dauth="no"
+fi
+AC_MSG_RESULT($disable_dauth)
+
+AM_CONDITIONAL(ENABLE_DAUTH, [test "x$disable_dauth" != "xyes" -a "$gcrypt" = 
"true"])
+
 MHD_LIB_LDFLAGS="-export-dynamic -no-undefined"
 
 # TODO insert a proper check here
@@ -393,12 +426,13 @@
   libgcrypt:         ${MSG_GCRYPT}
   libcurl (testing): ${MSG_CURL}
   Target directory:  ${prefix}
-  Messages disabled: ${disable_messages}
-  Postproc disabled: ${disable_postprocessor}
-  HTTPS disabled:    ${disable_https}
+  Messages:          ${enable_messages}
+  HTTP Digest Auth:  ${enable_dauth}
+  Postproc:          ${enable_postprocessor}
+  HTTPS support:     ${enable_https}
 ])
 
-if test "$disable_https" = "no"
+if test "x$enable_https" = "xyes"
 then
  AC_MSG_NOTICE([HTTPS subsystem configuration:
   License         :  LGPL only

Modified: libmicrohttpd/src/daemon/Makefile.am
===================================================================
--- libmicrohttpd/src/daemon/Makefile.am        2010-08-22 13:36:42 UTC (rev 
12708)
+++ libmicrohttpd/src/daemon/Makefile.am        2010-08-22 14:49:48 UTC (rev 
12709)
@@ -13,9 +13,6 @@
 lib_LTLIBRARIES = \
   libmicrohttpd.la
 
-if HAVE_POSTPROCESSOR
-  SUPPORT_POSTPROCESSOR = postprocessor.c
-endif
 
 libmicrohttpd_la_SOURCES = \
   connection.c connection.h \
@@ -23,7 +20,6 @@
   daemon.c  \
   internal.c internal.h \
   memorypool.c memorypool.h \
-  $(SUPPORT_POSTPROCESSOR) \
   response.c response.h
 libmicrohttpd_la_LDFLAGS = \
   $(MHD_LIB_LDFLAGS) \
@@ -33,7 +29,16 @@
   AM_CFLAGS = --coverage
 endif
 
+if HAVE_POSTPROCESSOR
+libmicrohttpd_la_SOURCES += \
+  postprocessor.c
+endif
 
+if ENABLE_DAUTH
+libmicrohttpd_la_SOURCES += \
+  digestauth.c
+endif
+
 if ENABLE_HTTPS
 libmicrohttpd_la_SOURCES += \
   connection_https.c connection_https.h

Added: libmicrohttpd/src/daemon/digestauth.c
===================================================================
--- libmicrohttpd/src/daemon/digestauth.c                               (rev 0)
+++ libmicrohttpd/src/daemon/digestauth.c       2010-08-22 14:49:48 UTC (rev 
12709)
@@ -0,0 +1,601 @@
+/*
+     This file is part of libmicrohttpd
+     (C) 2010 Daniel Pittman and Christian Grothoff
+
+     This library is free software; you can redistribute it and/or
+     modify it under the terms of the GNU Lesser General Public
+     License as published by the Free Software Foundation; either
+     version 2.1 of the License, or (at your option) any later version.
+
+     This library is distributed in the hope that it will be useful,
+     but WITHOUT ANY WARRANTY; without even the implied warranty of
+     MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the GNU
+     Lesser General Public License for more details.
+
+     You should have received a copy of the GNU Lesser General Public
+     License along with this library; if not, write to the Free Software
+     Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA  02110-1301 
 USA
+*/
+
+/**
+ * @file digestauth.c
+ * @brief Implements HTTP/1.1 Digest Auth according to RFC2617
+ * @author Amr Ali
+ */
+
+#include <stdlib.h>
+#include <gcrypt.h>
+#include <string.h>
+#include <time.h>
+#include "internal.h"
+#include "connection.h"
+#include "microhttpd.h"
+
+#define HASH_MD5_LEN 16
+#define HASH_SHA1_LEN 20
+#define HASH_MD5_HEX_LEN 32
+#define HASH_SHA1_HEX_LEN 40
+
+#define _OPAQUE        "opaque=\"11733b200778ce33060f31c9af70a870ba96ddd4\""
+#define _QOP           "qop=\"auth\""
+#define _STALE         "stale=true"
+#define _BASE          "Digest "
+#define _REALM         "realm="
+#define _NONCE         "nonce="
+
+/* convert bin to hex */
+static void
+cvthex(const unsigned char *bin, size_t len, char *hex)
+{
+       size_t i;
+       unsigned int j;
+
+       for (i = 0; i < len; ++i) {
+               j = (bin[i] >> 4) & 0x0f;
+
+               hex[i * 2] = j <= 9 ? (j + '0') : (j + 'a' - 10);
+
+               j = bin[i] & 0x0f;
+
+               hex[i * 2 + 1] = j <= 9 ? (j + '0') : (j + 'a' - 10);
+       }
+
+       hex[len * 2] = '\0';
+}
+
+/* calculate H(A1) as per RFC2617 spec */
+static int
+digest_calc_ha1(const char *alg,
+               const char *username,
+               const char *realm,
+               const char *password,
+               const char *nonce,
+               const char *cnonce,
+               char *sessionkey)
+{
+       gcry_md_hd_t md5;
+       gcry_error_t gerror;
+       unsigned char *ha1;
+
+       gerror = gcry_md_open(&md5, GCRY_MD_MD5, GCRY_MD_FLAG_SECURE);
+
+       if (gerror) return gerror;
+
+       gcry_md_write(md5, username, strlen(username));
+       gcry_md_write(md5, ":", 1);
+       gcry_md_write(md5, realm, strlen(realm));
+       gcry_md_write(md5, ":", 1);
+       gcry_md_write(md5, password, strlen(password));
+       gcry_md_final(md5);
+
+       ha1 = gcry_md_read(md5, GCRY_MD_MD5);
+
+       if (strcasecmp(alg, "md5-sess") == 0) {
+               gcry_md_reset(md5);
+               gcry_md_write(md5, ha1, HASH_MD5_LEN);
+               gcry_md_write(md5, ":", 1);
+               gcry_md_write(md5, nonce, strlen(nonce));
+               gcry_md_write(md5, ":", 1);
+               gcry_md_write(md5, cnonce, strlen(cnonce));
+               gcry_md_final(md5);
+
+               ha1 = gcry_md_read(md5, GCRY_MD_MD5);
+       }
+
+       cvthex(ha1, HASH_MD5_LEN, sessionkey);
+       gcry_md_close(md5);
+
+       return 0;
+}
+
+/* calculate request-digest/response-digest as per RFC2617 spec */
+static int
+digest_calc_response(const char *ha1,                  /* H(A1) */
+                    const char *nonce,                 /* nonce from server */
+                    const char *noncecount,            /* 8 hex digits */
+                    const char *cnonce,                        /* client nonce 
*/
+                    const char *qop,                   /* qop-value: "", 
"auth", "auth-int" */
+                    const char *method,                        /* method from 
the request */
+                    const char *uri,                   /* requested URL */
+                    const char *hentity,               /* H(entity body) if 
qop="auth-int" */
+                    char *response                             /* 
request-digest or response-digest */
+                    )
+{
+       gcry_md_hd_t md5;
+       gcry_error_t gerror;
+       unsigned char *ha2;
+       unsigned char *resphash;
+       char ha2hex[HASH_MD5_HEX_LEN + 1];
+
+       gerror = gcry_md_open(&md5, GCRY_MD_MD5, GCRY_MD_FLAG_SECURE);
+
+       if (gerror) return gerror;
+
+       /*
+        * calculate H(A2)
+        */
+
+       gcry_md_write(md5, method, strlen(method));
+       gcry_md_write(md5, ":", 1);
+       gcry_md_write(md5, uri, strlen(uri));
+
+       if (strcasecmp(qop, "auth-int") == 0) {
+               gcry_md_write(md5, ":", 1);
+               gcry_md_write(md5, hentity, strlen(hentity));
+       }
+
+       gcry_md_final(md5);
+
+       ha2 = gcry_md_read(md5, GCRY_MD_MD5);
+
+       cvthex(ha2, HASH_MD5_LEN, ha2hex);
+       gcry_md_reset(md5);
+
+       /*
+        * calculate response
+        */
+
+       gcry_md_write(md5, ha1, HASH_MD5_HEX_LEN);
+       gcry_md_write(md5, ":", 1);
+       gcry_md_write(md5, nonce, strlen(nonce));
+       gcry_md_write(md5, ":", 1);
+
+       if (*qop) {
+               gcry_md_write(md5, noncecount, strlen(noncecount));
+               gcry_md_write(md5, ":", 1);
+               gcry_md_write(md5, cnonce, strlen(cnonce));
+               gcry_md_write(md5, ":", 1);
+               gcry_md_write(md5, qop, strlen(qop));
+               gcry_md_write(md5, ":", 1);
+       }
+
+       gcry_md_write(md5, ha2hex, HASH_MD5_HEX_LEN);
+       gcry_md_final(md5);
+
+       resphash = gcry_md_read(md5, GCRY_MD_MD5);
+
+       cvthex(resphash, HASH_MD5_LEN, response);
+       gcry_md_close(md5);
+
+       return 0;
+}
+
+static const char *
+lookup_sub_value(char *data, size_t len, const char *key)
+{
+       char *tmp = data;
+       char *value = NULL;
+       size_t keylen;
+       size_t i;
+
+       keylen = strlen(key);
+
+       for (i = 0; i < len; ++i) {
+               if (strncmp(tmp, key, keylen) == 0 &&
+                               strncmp(tmp + keylen, "=", 1) == 0) {
+                       tmp += keylen;
+                       break;
+               } else {
+                       tmp++;
+               }
+
+               if ((i + 1) == len) return NULL;
+       }
+
+       while (1) {
+               tmp++;
+
+               if (*tmp == '"' && *(tmp + 1) == ',') {
+                       *tmp = '\0';
+                       break;
+               }
+
+               if (*tmp == '"' && *(tmp + 1) == '\0') {
+                       *tmp = '\0';
+                       break;
+               }
+
+               if (*tmp == ',' || *tmp == '\0') {
+                       *tmp = '\0';
+                       break;
+               }
+
+               if (*tmp == '"') continue;
+
+               if (value == NULL)
+                       value = tmp;
+       }
+
+       return value;
+}
+
+
+/**
+ * Get the username from the authorization header sent by the client
+ *
+ * @param connection The MHD connection structure
+ * @return NULL if no username could be found, a pointer
+ *                     to the username if found
+ */
+char *
+MHD_digest_auth_get_username(struct MHD_Connection *connection)
+{
+       size_t len;
+       size_t userlen;
+       char *buffer;
+       char *username;
+       const char *user;
+       const char *header;
+
+       header = MHD_lookup_connection_value(connection,
+                       MHD_HEADER_KIND, MHD_HTTP_HEADER_AUTHORIZATION);
+
+       if (header == NULL) return NULL;
+       if (strncmp(header, _BASE, strlen(_BASE)) != 0) return NULL;
+
+       len = strlen(header) - strlen(_BASE) + 1;
+       buffer = malloc(len);
+
+       if (buffer == NULL) return NULL;
+
+       strncpy(buffer, header + strlen(_BASE), len);
+
+       user = lookup_sub_value(buffer, len, "username");
+
+       if (user) {
+               userlen = strlen(user) + 1;
+               username = malloc(userlen);
+
+               if (username != NULL) {
+                       strncpy(username, user, userlen);
+                       free(buffer);
+                       return username;
+               }
+       }
+
+       free(buffer);
+       return NULL;
+}
+
+/**
+ * Authenticates the authorization header sent by the client
+ *
+ * @param connection The MHD connection structure
+ * @param realm The realm presented to the client
+ * @param username The username needs to be authenticated
+ * @param password The password used in the authentication
+ * @param nonce_timeout The amount of time for a nonce to be
+ *                     invalid in seconds
+ * @return MHD_YES if authenticated, MHD_NO if not,
+ *                     MHD_INVALID_NONCE if nonce is invalid
+ */
+int
+MHD_digest_auth_check(struct MHD_Connection *connection,
+                     const char *realm,
+                     const char *username,
+                     const char *password,
+                     unsigned int nonce_timeout)
+{
+       int auth;
+       size_t len;
+       char *buffer;
+       const char *header;
+       const char *ret;
+       const char *nonce;
+       const char *cnonce;
+       const char *uri;
+       const char *qop;
+       const char *nc;
+       const char *response;
+       unsigned char *tmpnonce;
+       char *hentity = NULL; /* "auth-int" is not supported */
+       char timestamp[5];
+       char ha1[HASH_MD5_HEX_LEN + 1];
+       char respexp[HASH_MD5_HEX_LEN + 1];
+       char noncehashexp[HASH_SHA1_HEX_LEN + 9];
+       unsigned int nonce_time;
+       time_t t;
+       gcry_error_t gerror;
+       gcry_md_hd_t sha1;
+
+       header = MHD_lookup_connection_value(
+                       connection, MHD_HEADER_KIND,
+                       MHD_HTTP_HEADER_AUTHORIZATION);
+
+       if (header == NULL) return MHD_NO;
+       if (strncmp(header, _BASE, strlen(_BASE)) != 0) return MHD_NO;
+
+       len = strlen(header) - strlen(_BASE) + 1;
+
+       buffer = malloc(len);
+
+       if (buffer == NULL) return MHD_NO;
+
+       strncpy(buffer, header + strlen(_BASE), len);
+
+       ret = lookup_sub_value(buffer, len, "username");
+
+       if (ret == NULL || strcmp(username, ret) != 0) {
+               free(buffer);
+               return MHD_NO;
+       }
+
+       ret = lookup_sub_value(buffer, len, "realm");
+
+       if (ret == NULL || strcmp(realm, ret) != 0) {
+               free(buffer);
+               return MHD_NO;
+       }
+
+       if ((uri = lookup_sub_value(buffer, len, "uri")) == NULL) {
+               free(buffer);
+               return MHD_NO;
+       }
+
+       if ((nonce = lookup_sub_value(buffer, len, "nonce")) == NULL) {
+               free(buffer);
+               return MHD_NO;
+       }
+
+       /*
+        * 8 = 4 hexadecimal numbers for the timestamp
+        */
+
+       nonce_time = strtoul(nonce + strlen(nonce) - 8, 0, 16);
+
+       time(&t);
+
+       /*
+        * First level vetting for the nonce validity
+        * if the timestamp attached to the nonce
+        * exceeds `nonce_timeout' then the nonce is
+        * invalid.
+        */
+
+       if (t - nonce_time > nonce_timeout) {
+               free(buffer);
+               return MHD_INVALID_NONCE;
+       }
+
+       gerror = gcry_md_open(&sha1, GCRY_MD_SHA1, GCRY_MD_FLAG_SECURE);
+
+       if (gerror) {
+               free(buffer);
+               return MHD_NO;
+       }
+
+       timestamp[0] = (nonce_time & 0xff000000) >> 0x18;
+       timestamp[1] = (nonce_time & 0x00ff0000) >> 0x10;
+       timestamp[2] = (nonce_time & 0x0000ff00) >> 0x08;
+       timestamp[3] = nonce_time & 0x000000ff;
+       timestamp[4] = '\0';
+
+       gcry_md_write(sha1, timestamp, 4);
+       gcry_md_write(sha1, ":", 1);
+       gcry_md_write(sha1, connection->method, strlen(connection->method));
+       gcry_md_write(sha1, ":", 1);
+       gcry_md_write(sha1, password, strlen(password));
+       gcry_md_write(sha1, ":", 1);
+       gcry_md_write(sha1, uri, strlen(uri));
+       gcry_md_write(sha1, ":", 1);
+       gcry_md_write(sha1, realm, strlen(realm));
+       gcry_md_final(sha1);
+
+       tmpnonce = gcry_md_read(sha1, GCRY_MD_SHA1);
+
+       cvthex(tmpnonce, HASH_SHA1_LEN, noncehashexp);
+       gcry_md_close(sha1);
+
+       strncat(noncehashexp, nonce + strlen(nonce) - 8, 8);
+
+       /*
+        * Second level vetting for the nonce validity
+        * if the timestamp attached to the nonce is valid
+        * and possibility fabricated (in case of an attack)
+        * the attacker must also know the password to be
+        * able to generate a "sane" nonce, which if he does
+        * not, the nonce fabrication process going to be
+        * very hard to achieve.
+        */
+
+       if (strncmp(nonce, noncehashexp, strlen(nonce)) != 0) {
+               free(buffer);
+               return MHD_INVALID_NONCE;
+       }
+
+       if ((cnonce = lookup_sub_value(buffer, len, "cnonce")) == NULL) {
+               free(buffer);
+               return MHD_NO;
+       }
+
+       if ((qop = lookup_sub_value(buffer, len, "qop")) == NULL) {
+               free(buffer);
+               return MHD_NO;
+       }
+
+       if ((nc = lookup_sub_value(buffer, len, "nc")) == NULL) {
+               free(buffer);
+               return MHD_NO;
+       }
+
+       if ((response = lookup_sub_value(buffer, len, "response")) == NULL) {
+               free(buffer);
+               return MHD_NO;
+       }
+
+       auth = digest_calc_ha1(
+                       "md5",
+                       username,
+                       realm,
+                       password,
+                       nonce,
+                       cnonce,
+                       ha1
+                       );
+
+       if (auth) {
+               free(buffer);
+               return MHD_NO;
+       }
+
+       auth = digest_calc_response(
+                       ha1,
+                       nonce,
+                       nc,
+                       cnonce,
+                       qop,
+                       connection->method,
+                       uri,
+                       hentity,
+                       respexp
+                       );
+
+       if (auth) {
+               free(buffer);
+               return MHD_NO;
+       }
+
+       auth = strcmp(response, respexp) == 0 ? MHD_YES : MHD_NO;
+
+       free(buffer);
+
+       return auth;
+}
+
+/**
+ * Queues a response to request authentication from the client
+ *
+ * @param connection The MHD connection structure
+ * @param realm The realm presented to the client
+ * @param password The password used in authentication
+ * @param signal_stale MHD_YES if the nonce is invalid to add
+ *                     'stale=true' to the authentication header
+ * @return MHD_YES on success, MHD_NO otherwise
+ */
+int
+MHD_queue_auth_fail_response(struct MHD_Connection *connection,
+                            const char *realm,
+                            const char *password,
+                            const char *opaque,
+                            int signal_stale)
+{
+       int ret;
+       size_t hlen;
+       unsigned char *tmpnonce;
+       char *header;
+       unsigned char timestamp[5];
+       char timestamphex[9];
+       char nonce[HASH_SHA1_HEX_LEN + 9];
+       time_t t;
+       struct MHD_Response *response;
+       gcry_error_t gerror;
+       gcry_md_hd_t sha1;
+
+       response = MHD_create_response_from_data(0, NULL, MHD_NO, MHD_NO);
+
+       if (!response) return MHD_NO;
+
+       /*
+        * Generating the server nonce
+        */
+
+       gerror = gcry_md_open(&sha1, GCRY_MD_SHA1, GCRY_MD_FLAG_SECURE);
+
+       if (gerror) return MHD_NO;
+
+       time(&t);
+
+       timestamp[0] = (t & 0xff000000) >> 0x18;
+       timestamp[1] = (t & 0x00ff0000) >> 0x10;
+       timestamp[2] = (t & 0x0000ff00) >> 0x08;
+       timestamp[3] = t & 0x000000ff;
+       timestamp[4] = '\0';
+
+       gcry_md_write(sha1, timestamp, 4);
+       gcry_md_write(sha1, ":", 1);
+       gcry_md_write(sha1, connection->method, strlen(connection->method));
+       gcry_md_write(sha1, ":", 1);
+       gcry_md_write(sha1, password, strlen(password));
+       gcry_md_write(sha1, ":", 1);
+       gcry_md_write(sha1, connection->url, strlen(connection->url));
+       gcry_md_write(sha1, ":", 1);
+       gcry_md_write(sha1, realm, strlen(realm));
+       gcry_md_final(sha1);
+
+       tmpnonce = gcry_md_read(sha1, GCRY_MD_SHA1);
+
+       cvthex(timestamp, 4, timestamphex);
+       cvthex(tmpnonce, HASH_SHA1_LEN, nonce);
+       strncat(nonce, timestamphex, 8);
+       gcry_md_close(sha1);
+
+       /*
+        * Building the authentication header
+        */
+
+       /* 4(single quotes) + 3(commas) + NULL = 8 */
+       hlen = strlen(_BASE) + strlen(_REALM) + strlen(realm) +
+               strlen(_QOP) + strlen(_NONCE) + strlen(nonce) +
+               strlen(_OPAQUE) + 8;
+
+       /* 1(comma for stale=true) */
+       if (signal_stale)
+               hlen += strlen(_STALE) + 1;
+
+       header = malloc(hlen);
+
+       if (header == NULL) return MHD_NO;
+
+       snprintf(header, hlen,
+                       "%s%s\"%s\",%s,%s\"%s\",%s",
+                       _BASE, _REALM, realm, _QOP,
+                       _NONCE, nonce, _OPAQUE);
+
+       /* Add "stale=true" to the authentication header if nonce is invalid */
+       if (signal_stale) {
+               strncat(header, ",", 1);
+               strncat(header, _STALE, strlen(_STALE));
+       }
+
+       /*
+        * Sending response with authentication header
+        */
+
+       ret = MHD_add_response_header(response,
+                       MHD_HTTP_HEADER_WWW_AUTHENTICATE, header);
+
+       free(header);
+
+       if(!ret) {
+               MHD_destroy_response(response);
+               return MHD_NO;
+       }
+
+       ret = MHD_queue_response(connection, MHD_HTTP_UNAUTHORIZED, response);
+
+       MHD_destroy_response(response);
+
+       return ret;
+}
+
+/* end of digestauth.c */

Modified: libmicrohttpd/src/examples/Makefile.am
===================================================================
--- libmicrohttpd/src/examples/Makefile.am      2010-08-22 13:36:42 UTC (rev 
12708)
+++ libmicrohttpd/src/examples/Makefile.am      2010-08-22 14:49:48 UTC (rev 
12709)
@@ -28,6 +28,10 @@
 noinst_PROGRAMS += https_fileserver_example 
 endif
 
+if ENABLE_DAUTH
+noinst_PROGRAMS += digest_auth_example 
+endif
+
 minimal_example_SOURCES = \
  minimal_example.c 
 minimal_example_LDADD = \
@@ -43,6 +47,11 @@
 authorization_example_LDADD = \
  $(top_builddir)/src/daemon/libmicrohttpd.la 
 
+digest_auth_example_SOURCES = \
+ digest_auth_example.c 
+digest_auth_example_LDADD = \
+ $(top_builddir)/src/daemon/libmicrohttpd.la 
+
 refuse_post_example_SOURCES = \
  refuse_post_example.c 
 refuse_post_example_LDADD = \

Added: libmicrohttpd/src/examples/digest_auth_example.c
===================================================================
--- libmicrohttpd/src/examples/digest_auth_example.c                            
(rev 0)
+++ libmicrohttpd/src/examples/digest_auth_example.c    2010-08-22 14:49:48 UTC 
(rev 12709)
@@ -0,0 +1,105 @@
+/*
+     This file is part of libmicrohttpd
+     (C) 2010 Christian Grothoff (and other contributing authors)
+
+     This library is free software; you can redistribute it and/or
+     modify it under the terms of the GNU Lesser General Public
+     License as published by the Free Software Foundation; either
+     version 2.1 of the License, or (at your option) any later version.
+
+     This library is distributed in the hope that it will be useful,
+     but WITHOUT ANY WARRANTY; without even the implied warranty of
+     MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the GNU
+     Lesser General Public License for more details.
+
+     You should have received a copy of the GNU Lesser General Public
+     License along with this library; if not, write to the Free Software
+     Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA  02110-1301 
 USA
+*/
+/**
+ * @file digest_auth_example.c
+ * @brief minimal example for how to use digest auth with libmicrohttpd
+ * @author Amr Ali
+ */
+
+#include "platform.h"
+#include <microhttpd.h>
+#include <stdlib.h>
+
+#define PAGE "<html><head><title>libmicrohttpd 
demo</title></head><body>libmicrohttpd demo</body></html>"
+
+#define OPAQUE "11733b200778ce33060f31c9af70a870ba96ddd4"
+
+static int
+ahc_echo (void *cls,
+          struct MHD_Connection *connection,
+          const char *url,
+          const char *method,
+          const char *version,
+          const char *upload_data, size_t *upload_data_size, void **ptr)
+{
+  struct MHD_Response *response;
+  char *username;
+  const char *password = "testpass";
+  const char *realm = "address@hidden";
+  int ret;
+
+  username = MHD_digest_auth_get_username(connection);
+
+  if (username == NULL) {
+         ret = MHD_queue_auth_fail_response(connection, realm,
+                                            password, 
+                                            OPAQUE,
+                                            MHD_NO);
+
+         return ret;
+  }
+
+  ret = MHD_digest_auth_check(connection, realm,
+                 username, password, 300);
+
+  free(username);
+
+  if (ret == MHD_INVALID_NONCE) {
+         ret = MHD_queue_auth_fail_response(connection, realm,
+                                            password,
+                                            OPAQUE, MHD_YES);
+
+         return ret;
+  }
+
+  if (ret == MHD_NO) {
+         ret = MHD_queue_auth_fail_response(connection, realm,
+                                            password, OPAQUE, MHD_NO);
+         
+         return ret;
+  }
+  
+  response = MHD_create_response_from_data(strlen(PAGE), PAGE,
+                 MHD_NO, MHD_NO);
+
+  ret = MHD_queue_response(connection, MHD_HTTP_OK, response);
+
+  MHD_destroy_response(response);
+  return ret;
+}
+
+int
+main (int argc, char *const *argv)
+{
+  struct MHD_Daemon *d;
+
+  if (argc != 2)
+    {
+      printf ("%s PORT\n", argv[0]);
+      return 1;
+    }
+  d = MHD_start_daemon (MHD_USE_THREAD_PER_CONNECTION | MHD_USE_DEBUG,
+                        atoi (argv[1]),
+                        NULL, NULL, &ahc_echo, PAGE, MHD_OPTION_END);
+  if (d == NULL)
+    return 1;
+  (void) getc (stdin);
+  MHD_stop_daemon (d);
+  return 0;
+}

Modified: libmicrohttpd/src/include/microhttpd.h
===================================================================
--- libmicrohttpd/src/include/microhttpd.h      2010-08-22 13:36:42 UTC (rev 
12708)
+++ libmicrohttpd/src/include/microhttpd.h      2010-08-22 14:49:48 UTC (rev 
12709)
@@ -94,6 +94,11 @@
 #define MHD_NO 0
 
 /**
+ * MHD digest auth internal code for an invalid nonce.
+ */
+#define MHD_INVALID_NONCE -1
+
+/**
  * Constant used to indicate unknown size (use when
  * creating a response).
  */
@@ -1215,7 +1220,66 @@
 int MHD_destroy_post_processor (struct MHD_PostProcessor *pp);
 
 
+/* ********************* Digest Authentication functions *************** */
 
+
+/**
+ * Constant to indicate that the nonce of the provided
+ * authentication code was wrong.
+ */
+#define MHD_INVALID_NONCE -1
+
+
+/**
+ * Get the username from the authorization header sent by the client
+ *
+ * @param connection The MHD connection structure
+ * @return NULL if no username could be found, a pointer
+ *                     to the username if found
+ */
+char *
+MHD_digest_auth_get_username(struct MHD_Connection *connection);
+
+
+/**
+ * Authenticates the authorization header sent by the client
+ *
+ * @param connection The MHD connection structure
+ * @param realm The realm presented to the client
+ * @param username The username needs to be authenticated
+ * @param password The password used in the authentication
+ * @param nonce_timeout The amount of time for a nonce to be
+ *                     invalid in seconds
+ * @return MHD_YES if authenticated, MHD_NO if not,
+ *                     MHD_INVALID_NONCE if nonce is invalid
+ */
+int
+MHD_digest_auth_check(struct MHD_Connection *connection,
+                     const char *realm,
+                     const char *username,
+                     const char *password,
+                     unsigned int nonce_timeout);
+
+
+/**
+ * Queues a response to request authentication from the client
+ *
+ * @param connection The MHD connection structure
+ * @param realm The realm presented to the client
+ * @param password The password used in authentication
+ * @param opaque string to user for opaque value
+ * @param signal_stale MHD_YES if the nonce is invalid to add
+ *                     'stale=true' to the authentication header
+ * @return MHD_YES on success, MHD_NO otherwise
+ */
+int
+MHD_queue_auth_fail_response(struct MHD_Connection *connection,
+                            const char *realm,
+                            const char *password,
+                            const char *opaque,
+                            int signal_stale);
+
+
 /* ********************** generic query functions ********************** */
 
 /**




reply via email to

[Prev in Thread] Current Thread [Next in Thread]