digestauth.c

Go to the documentation of this file.
00001 /*
00002      This file is part of libmicrohttpd
00003      (C) 2010, 2011, 2012 Daniel Pittman and Christian Grothoff
00004 
00005      This library is free software; you can redistribute it and/or
00006      modify it under the terms of the GNU Lesser General Public
00007      License as published by the Free Software Foundation; either
00008      version 2.1 of the License, or (at your option) any later version.
00009 
00010      This library is distributed in the hope that it will be useful,
00011      but WITHOUT ANY WARRANTY; without even the implied warranty of
00012      MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the GNU
00013      Lesser General Public License for more details.
00014 
00015      You should have received a copy of the GNU Lesser General Public
00016      License along with this library; if not, write to the Free Software
00017      Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA  02110-1301  USA
00018 */
00019 
00027 #include "platform.h"
00028 #include <limits.h>
00029 #include "internal.h"
00030 #include "md5.h"
00031 #include "base64.h"
00032 
00033 #define HASH_MD5_HEX_LEN (2 * MD5_DIGEST_SIZE)
00034 
00038 #define _BASE           "Digest "
00039 
00043 #define _BASIC_BASE             "Basic "
00044 
00048 #define MAX_USERNAME_LENGTH 128
00049 
00053 #define MAX_REALM_LENGTH 256
00054 
00058 #define MAX_AUTH_RESPONSE_LENGTH 128
00059 
00067 static void
00068 cvthex(const unsigned char *bin,
00069        size_t len,
00070        char *hex)
00071 {
00072   size_t i;
00073   unsigned int j;
00074   
00075   for (i = 0; i < len; ++i) 
00076     {
00077       j = (bin[i] >> 4) & 0x0f;      
00078       hex[i * 2] = j <= 9 ? (j + '0') : (j + 'a' - 10);    
00079       j = bin[i] & 0x0f;    
00080       hex[i * 2 + 1] = j <= 9 ? (j + '0') : (j + 'a' - 10);
00081     }
00082   hex[len * 2] = '\0';
00083 }
00084 
00097 static void
00098 digest_calc_ha1(const char *alg,
00099                 const char *username,
00100                 const char *realm,
00101                 const char *password,
00102                 const char *nonce,
00103                 const char *cnonce,
00104                 char *sessionkey)
00105 {
00106   struct MD5Context md5;
00107   unsigned char ha1[MD5_DIGEST_SIZE];
00108   
00109   MD5Init (&md5);
00110   MD5Update (&md5, username, strlen (username));
00111   MD5Update (&md5, ":", 1);
00112   MD5Update (&md5, realm, strlen (realm));
00113   MD5Update (&md5, ":", 1);
00114   MD5Update (&md5, password, strlen (password));
00115   MD5Final (ha1, &md5);
00116   if (0 == strcasecmp(alg, "md5-sess")) 
00117     {
00118       MD5Init (&md5);
00119       MD5Update (&md5, ha1, sizeof (ha1));
00120       MD5Update (&md5, ":", 1);
00121       MD5Update (&md5, nonce, strlen (nonce));
00122       MD5Update (&md5, ":", 1);
00123       MD5Update (&md5, cnonce, strlen (cnonce));
00124       MD5Final (ha1, &md5);
00125     }
00126   cvthex(ha1, sizeof (ha1), sessionkey);
00127 }
00128 
00129 
00143 static void
00144 digest_calc_response(const char *ha1,
00145                      const char *nonce,
00146                      const char *noncecount,
00147                      const char *cnonce,
00148                      const char *qop,
00149                      const char *method,
00150                      const char *uri,
00151                      const char *hentity,
00152                      char *response)
00153 {
00154   struct MD5Context md5;
00155   unsigned char ha2[MD5_DIGEST_SIZE];
00156   unsigned char resphash[MD5_DIGEST_SIZE];
00157   char ha2hex[HASH_MD5_HEX_LEN + 1];
00158   
00159   MD5Init (&md5);
00160   MD5Update (&md5, method, strlen(method));
00161   MD5Update (&md5, ":", 1);
00162   MD5Update (&md5, uri, strlen(uri)); 
00163 #if 0
00164   if (strcasecmp(qop, "auth-int") == 0) 
00165     {
00166       /* This is dead code since the rest of this module does
00167          not support auth-int. */
00168       MD5Update (&md5, ":", 1);
00169       if (hentity != NULL)
00170         MD5Update (&md5, hentity, strlen(hentity));
00171     }
00172 #endif  
00173   MD5Final (ha2, &md5);
00174   cvthex(ha2, MD5_DIGEST_SIZE, ha2hex);
00175   MD5Init (&md5);  
00176   /* calculate response */  
00177   MD5Update (&md5, ha1, HASH_MD5_HEX_LEN);
00178   MD5Update (&md5, ":", 1);
00179   MD5Update (&md5, nonce, strlen(nonce));
00180   MD5Update (&md5, ":", 1);  
00181   if ('\0' != *qop)
00182     {
00183       MD5Update (&md5, noncecount, strlen(noncecount));
00184       MD5Update (&md5, ":", 1);
00185       MD5Update (&md5, cnonce, strlen(cnonce));
00186       MD5Update (&md5, ":", 1);
00187       MD5Update (&md5, qop, strlen(qop));
00188       MD5Update (&md5, ":", 1);
00189     }  
00190   MD5Update (&md5, ha2hex, HASH_MD5_HEX_LEN);
00191   MD5Final (resphash, &md5);
00192   cvthex(resphash, sizeof (resphash), response);
00193 }
00194 
00195 
00210 static int
00211 lookup_sub_value(char *dest,
00212                  size_t size,
00213                  const char *data,
00214                  const char *key)
00215 {
00216   size_t keylen = strlen(key);
00217   size_t len;
00218   const char *ptr = data;
00219   const char *eq;
00220   const char *q1;
00221   const char *q2;
00222   const char *qn;
00223 
00224   if (0 == size)
00225     return 0;
00226   while ('\0' != *ptr)
00227     {
00228       if (NULL == (eq = strstr (ptr, "=")))
00229         return 0;
00230       q1 = eq + 1;
00231       while (' ' == *q1)
00232         q1++;      
00233       if ('\"' != *q1)
00234         {
00235           q2 = strstr (q1, ",");
00236           qn = q2;
00237         }
00238       else
00239         {
00240           q1++;
00241           q2 = strstr (q1, "\"");
00242           if (NULL == q2)
00243             return 0; /* end quote not found */
00244           qn = q2 + 1;
00245         }      
00246       if ( (0 == strncasecmp (ptr,
00247                               key,
00248                               keylen)) &&
00249            (eq == &ptr[keylen]) )
00250         {
00251           if (q2 == NULL)
00252             {
00253               len = strlen (q1) + 1;
00254               if (size > len)
00255                 size = len;
00256               size--;
00257               strncpy (dest,
00258                        q1,
00259                        size);
00260               dest[size] = '\0';
00261               return size;
00262             }
00263           else
00264             {
00265               if (size > (q2 - q1) + 1)
00266                 size = (q2 - q1) + 1;
00267               size--;
00268               memcpy (dest, 
00269                       q1,
00270                       size);
00271               dest[size] = '\0';
00272               return size;
00273             }
00274         }
00275       if (NULL == qn)
00276         return 0;
00277       ptr = strstr (qn, ",");
00278       if (NULL == ptr)
00279         return 0;
00280       ptr++;
00281       while (' ' == *ptr)
00282         ptr++;
00283     }
00284   return 0;
00285 }
00286 
00287 
00297 static int
00298 check_nonce_nc (struct MHD_Connection *connection,
00299                 const char *nonce,
00300                 unsigned long int nc)
00301 {
00302   uint32_t off;
00303   uint32_t mod;
00304   const char *np;
00305 
00306   mod = connection->daemon->nonce_nc_size;
00307   if (0 == mod)
00308     return MHD_NO; /* no array! */
00309   /* super-fast xor-based "hash" function for HT lookup in nonce array */
00310   off = 0;
00311   np = nonce;
00312   while (*np != '\0')
00313     {
00314       off = (off << 8) | (*np ^ (off >> 24));
00315       np++;
00316     }
00317   off = off % mod;
00318   /*
00319    * Look for the nonce, if it does exist and its corresponding
00320    * nonce counter is less than the current nonce counter by 1,
00321    * then only increase the nonce counter by one.
00322    */
00323   
00324   pthread_mutex_lock(&connection->daemon->nnc_lock);
00325   if (nc == 0)
00326     {
00327       strcpy(connection->daemon->nnc[off].nonce, 
00328              nonce);
00329       connection->daemon->nnc[off].nc = 0;  
00330       pthread_mutex_unlock(&connection->daemon->nnc_lock);
00331       return MHD_YES;
00332     }
00333   if ( (nc <= connection->daemon->nnc[off].nc) ||
00334        (0 != strcmp(connection->daemon->nnc[off].nonce, nonce)) )
00335     {
00336       pthread_mutex_unlock(&connection->daemon->nnc_lock);
00337 #if HAVE_MESSAGES
00338       MHD_DLOG (connection->daemon, 
00339                 "Stale nonce received.  If this happens a lot, you should probably increase the size of the nonce array.\n");
00340 #endif
00341       return MHD_NO;
00342     }
00343   connection->daemon->nnc[off].nc = nc;
00344   pthread_mutex_unlock(&connection->daemon->nnc_lock);
00345   return MHD_YES;
00346 }
00347 
00348 
00356 char *
00357 MHD_digest_auth_get_username(struct MHD_Connection *connection)
00358 {
00359   size_t len;
00360   char user[MAX_USERNAME_LENGTH];
00361   const char *header;
00362   
00363   header = MHD_lookup_connection_value(connection,
00364                                        MHD_HEADER_KIND, 
00365                                        MHD_HTTP_HEADER_AUTHORIZATION); 
00366   if (header == NULL)
00367     return NULL;
00368   if (strncmp(header, _BASE, strlen(_BASE)) != 0)
00369     return NULL;
00370   header += strlen (_BASE);
00371   len = lookup_sub_value(user,
00372                          sizeof (user),
00373                          header, 
00374                          "username");
00375   if (!len)
00376     return NULL;
00377   return strdup(user);
00378 }
00379 
00380 
00394 static void
00395 calculate_nonce (uint32_t nonce_time,
00396                  const char *method,
00397                  const char *rnd,
00398                  unsigned int rnd_size,
00399                  const char *uri,
00400                  const char *realm,
00401                  char *nonce)
00402 {
00403   struct MD5Context md5;
00404   unsigned char timestamp[4];
00405   unsigned char tmpnonce[MD5_DIGEST_SIZE];
00406   char timestamphex[sizeof(timestamp)*2+1];
00407 
00408   MD5Init (&md5);
00409   timestamp[0] = (nonce_time & 0xff000000) >> 0x18;
00410   timestamp[1] = (nonce_time & 0x00ff0000) >> 0x10;
00411   timestamp[2] = (nonce_time & 0x0000ff00) >> 0x08;
00412   timestamp[3] = (nonce_time & 0x000000ff);    
00413   MD5Update(&md5, timestamp, 4);
00414   MD5Update(&md5, ":", 1);
00415   MD5Update(&md5, method, strlen(method));
00416   MD5Update(&md5, ":", 1);
00417   if (rnd_size > 0)
00418     MD5Update(&md5, rnd, rnd_size);
00419   MD5Update(&md5, ":", 1);
00420   MD5Update(&md5, uri, strlen(uri));
00421   MD5Update(&md5, ":", 1);
00422   MD5Update(&md5, realm, strlen(realm));
00423   MD5Final (tmpnonce, &md5);  
00424   cvthex(tmpnonce, sizeof (tmpnonce), nonce);  
00425   cvthex(timestamp, 4, timestamphex);
00426   strncat(nonce, timestamphex, 8);
00427 }
00428 
00429 
00440 static int
00441 test_header (struct MHD_Connection *connection,
00442              const char *key,
00443              const char *value)
00444 {
00445   struct MHD_HTTP_Header *pos;
00446 
00447   for (pos = connection->headers_received; NULL != pos; pos = pos->next)
00448     {
00449       if (MHD_GET_ARGUMENT_KIND != pos->kind)
00450         continue;
00451       if (0 != strcmp (key, pos->header))
00452         continue;
00453       if ( (NULL == value) && (NULL == pos->value))
00454         return MHD_YES;
00455       if ( (NULL == value) || (NULL == pos->value))
00456         continue;
00457       if (0 != strcmp (value, pos->value))
00458         continue;
00459       return MHD_YES;      
00460     }
00461   return MHD_NO;
00462 }
00463 
00464 
00475 static int
00476 check_argument_match (struct MHD_Connection *connection,
00477                       const char *args)
00478 {
00479   struct MHD_HTTP_Header *pos;
00480   size_t slen = strlen (args) + 1;
00481   char argb[slen];
00482   char *argp;
00483   char *equals;
00484   char *amper;
00485   unsigned int num_headers;
00486 
00487   num_headers = 0;
00488   memcpy (argb, args, slen);
00489   argp = argb;
00490   while ( (argp != NULL) &&
00491           (argp[0] != '\0') )
00492     {
00493       equals = strstr (argp, "=");
00494       if (equals == NULL) 
00495         {         
00496           /* add with 'value' NULL */
00497           connection->daemon->unescape_callback (connection->daemon->unescape_callback_cls,
00498                                                  connection,
00499                                                  argp);
00500           if (MHD_YES != test_header (connection, argp, NULL))
00501             return MHD_NO;
00502           num_headers++;
00503           break;
00504         }
00505       equals[0] = '\0';
00506       equals++;
00507       amper = strstr (equals, "&");
00508       if (amper != NULL)
00509         {
00510           amper[0] = '\0';
00511           amper++;
00512         }
00513       connection->daemon->unescape_callback (connection->daemon->unescape_callback_cls,
00514                                              connection,
00515                                              argp);
00516       connection->daemon->unescape_callback (connection->daemon->unescape_callback_cls,
00517                                              connection,
00518                                              equals);
00519       if (! test_header (connection, argp, equals))
00520         return MHD_NO;
00521       num_headers++;
00522       argp = amper;
00523     }
00524   
00525   /* also check that the number of headers matches */
00526   for (pos = connection->headers_received; NULL != pos; pos = pos->next)
00527     {
00528       if (MHD_GET_ARGUMENT_KIND != pos->kind)
00529         continue;
00530       num_headers--;
00531     }
00532   if (0 != num_headers)  
00533     return MHD_NO;
00534   return MHD_YES;
00535 }
00536 
00537 
00550 int
00551 MHD_digest_auth_check(struct MHD_Connection *connection,
00552                       const char *realm,
00553                       const char *username,
00554                       const char *password,
00555                       unsigned int nonce_timeout)
00556 {
00557   size_t len;
00558   const char *header;
00559   char *end;
00560   char nonce[MAX_NONCE_LENGTH];
00561   char cnonce[MAX_NONCE_LENGTH];
00562   char qop[15]; /* auth,auth-int */
00563   char nc[20];
00564   char response[MAX_AUTH_RESPONSE_LENGTH];
00565   const char *hentity = NULL; /* "auth-int" is not supported */
00566   char ha1[HASH_MD5_HEX_LEN + 1];
00567   char respexp[HASH_MD5_HEX_LEN + 1];
00568   char noncehashexp[HASH_MD5_HEX_LEN + 9];
00569   uint32_t nonce_time;
00570   uint32_t t;
00571   size_t left; /* number of characters left in 'header' for 'uri' */
00572   unsigned long int nci;
00573 
00574   header = MHD_lookup_connection_value(connection,
00575                                        MHD_HEADER_KIND,
00576                                        MHD_HTTP_HEADER_AUTHORIZATION);  
00577   if (header == NULL) 
00578     return MHD_NO;
00579   if (strncmp(header, _BASE, strlen(_BASE)) != 0) 
00580     return MHD_NO;
00581   header += strlen (_BASE);
00582   left = strlen (header);
00583 
00584   {
00585     char un[MAX_USERNAME_LENGTH];
00586     len = lookup_sub_value(un,
00587                            sizeof (un),
00588                            header, "username");
00589     if ( (!len) ||
00590          (strcmp(username, un) != 0) ) 
00591       return MHD_NO;
00592     left -= strlen ("username") + len;
00593   }
00594 
00595   {
00596     char r[MAX_REALM_LENGTH];
00597     len = lookup_sub_value(r, 
00598                            sizeof (r),
00599                            header, "realm");  
00600     if ( (!len) || 
00601          (strcmp(realm, r) != 0) )
00602       return MHD_NO;
00603     left -= strlen ("realm") + len;
00604   }
00605 
00606   if (0 == (len = lookup_sub_value(nonce, 
00607                                    sizeof (nonce),
00608                                    header, "nonce")))
00609     return MHD_NO;
00610   left -= strlen ("nonce") + len;
00611 
00612   {
00613     char uri[left];  
00614   
00615     if (0 == lookup_sub_value(uri,
00616                               sizeof (uri),
00617                               header, "uri")) 
00618       return MHD_NO;
00619       
00620     /* 8 = 4 hexadecimal numbers for the timestamp */  
00621     nonce_time = strtoul(nonce + len - 8, (char **)NULL, 16);  
00622     t = (uint32_t) MHD_monotonic_time();    
00623     /*
00624      * First level vetting for the nonce validity if the timestamp
00625      * attached to the nonce exceeds `nonce_timeout' then the nonce is
00626      * invalid.
00627      */
00628     if ( (t > nonce_time + nonce_timeout) ||
00629          (nonce_time + nonce_timeout < nonce_time) )
00630       return MHD_INVALID_NONCE;
00631     if (0 != strncmp (uri,
00632                       connection->url,
00633                       strlen (connection->url)))
00634     {
00635 #if HAVE_MESSAGES
00636       MHD_DLOG (connection->daemon, 
00637                 "Authentication failed, URI does not match.\n");
00638 #endif
00639       return MHD_NO;
00640     }
00641     {
00642       const char *args = strstr (uri, "?");
00643       if (args == NULL)
00644         args = "";
00645       else
00646         args++;
00647       if (MHD_YES !=
00648           check_argument_match (connection,
00649                                 args) ) 
00650       {
00651 #if HAVE_MESSAGES
00652         MHD_DLOG (connection->daemon, 
00653                   "Authentication failed, arguments do not match.\n");
00654 #endif
00655         return MHD_NO;
00656       }
00657     }
00658     calculate_nonce (nonce_time,
00659                      connection->method,
00660                      connection->daemon->digest_auth_random,
00661                      connection->daemon->digest_auth_rand_size,
00662                      connection->url,
00663                      realm,
00664                      noncehashexp);
00665     /*
00666      * Second level vetting for the nonce validity
00667      * if the timestamp attached to the nonce is valid
00668      * and possibly fabricated (in case of an attack)
00669      * the attacker must also know the random seed to be
00670      * able to generate a "sane" nonce, which if he does
00671      * not, the nonce fabrication process going to be
00672      * very hard to achieve.
00673      */
00674     
00675     if (0 != strcmp(nonce, noncehashexp))
00676       return MHD_INVALID_NONCE;
00677     if ( (0 == lookup_sub_value(cnonce,
00678                                 sizeof (cnonce), 
00679                                 header, "cnonce")) ||
00680          (0 == lookup_sub_value(qop, sizeof (qop), header, "qop")) ||
00681          ( (0 != strcmp (qop, "auth")) && 
00682            (0 != strcmp (qop, "")) ) ||
00683          (0 == lookup_sub_value(nc, sizeof (nc), header, "nc"))  ||
00684          (0 == lookup_sub_value(response, sizeof (response), header, "response")) )
00685     {
00686 #if HAVE_MESSAGES
00687       MHD_DLOG (connection->daemon, 
00688                 "Authentication failed, invalid format.\n");
00689 #endif
00690       return MHD_NO;
00691     }
00692     nci = strtoul (nc, &end, 16);
00693     if ( ('\0' != *end) ||
00694          ( (LONG_MAX == nci) && (errno == ERANGE) ) )
00695     {
00696 #if HAVE_MESSAGES
00697       MHD_DLOG (connection->daemon, 
00698                 "Authentication failed, invalid format.\n");
00699 #endif
00700       return MHD_NO; /* invalid nonce format */
00701     }
00702     /*
00703      * Checking if that combination of nonce and nc is sound
00704      * and not a replay attack attempt. Also adds the nonce
00705      * to the nonce-nc map if it does not exist there.
00706      */
00707     
00708     if (MHD_YES != check_nonce_nc (connection, nonce, nci))
00709       return MHD_NO;
00710     
00711     digest_calc_ha1("md5",
00712                     username,
00713                     realm,
00714                     password,
00715                     nonce,
00716                     cnonce,
00717                     ha1);
00718     digest_calc_response(ha1,
00719                          nonce,
00720                          nc,
00721                          cnonce,
00722                          qop,
00723                          connection->method,
00724                          uri,
00725                          hentity,
00726                          respexp);  
00727     return strcmp(response, respexp) == 0 ? MHD_YES : MHD_NO;
00728   }
00729 }
00730 
00731 
00742 int
00743 MHD_queue_auth_fail_response(struct MHD_Connection *connection,
00744                              const char *realm,
00745                              const char *opaque,
00746                              struct MHD_Response *response,
00747                              int signal_stale)
00748 {
00749   int ret;
00750   size_t hlen;
00751   char nonce[HASH_MD5_HEX_LEN + 9];
00752 
00753   /* Generating the server nonce */  
00754   calculate_nonce ((uint32_t) MHD_monotonic_time(),
00755                    connection->method,
00756                    connection->daemon->digest_auth_random,
00757                    connection->daemon->digest_auth_rand_size,
00758                    connection->url,
00759                    realm,
00760                    nonce);
00761   if (MHD_YES != check_nonce_nc (connection, nonce, 0))
00762     {
00763 #if HAVE_MESSAGES
00764       MHD_DLOG (connection->daemon, 
00765                 "Could not register nonce (is the nonce array size zero?).\n");
00766 #endif
00767       return MHD_NO;  
00768     }
00769   /* Building the authentication header */
00770   hlen = snprintf(NULL,
00771                   0,
00772                   "Digest realm=\"%s\",qop=\"auth\",nonce=\"%s\",opaque=\"%s\"%s",
00773                   realm, 
00774                   nonce,
00775                   opaque,
00776                   signal_stale ? ",stale=\"true\"" : "");
00777   {
00778     char header[hlen + 1];
00779     snprintf(header,
00780              sizeof(header),
00781              "Digest realm=\"%s\",qop=\"auth\",nonce=\"%s\",opaque=\"%s\"%s",
00782              realm, 
00783              nonce,
00784              opaque,
00785              signal_stale ? ",stale=\"true\"" : "");
00786     ret = MHD_add_response_header(response,
00787                                   MHD_HTTP_HEADER_WWW_AUTHENTICATE, 
00788                                   header);
00789   }
00790   if (MHD_YES == ret) 
00791     ret = MHD_queue_response(connection, 
00792                              MHD_HTTP_UNAUTHORIZED, 
00793                              response);  
00794   return ret;
00795 }
00796 
00797 
00806 char *
00807 MHD_basic_auth_get_username_password(struct MHD_Connection *connection,
00808                                      char** password) 
00809 {
00810   const char *header;
00811   char *decode;
00812   const char *separator;
00813   char *user;
00814   
00815   header = MHD_lookup_connection_value(connection, 
00816                                        MHD_HEADER_KIND,
00817                                        MHD_HTTP_HEADER_AUTHORIZATION);
00818   if (header == NULL)
00819     return NULL;
00820   if (strncmp(header, _BASIC_BASE, strlen(_BASIC_BASE)) != 0)
00821     return NULL;
00822   header += strlen(_BASIC_BASE);
00823   decode = BASE64Decode(header);
00824   if (decode == NULL) 
00825     {
00826 #if HAVE_MESSAGES
00827       MHD_DLOG(connection->daemon,
00828                "Error decoding basic authentication\n");
00829 #endif
00830       return NULL;
00831     }
00832   /* Find user:password pattern */
00833   separator = strstr(decode, ":");
00834   if (separator == NULL) 
00835     {
00836 #if HAVE_MESSAGES
00837       MHD_DLOG(connection->daemon,
00838                "Basic authentication doesn't contain ':' separator\n");
00839 #endif
00840       free(decode);
00841       return NULL;
00842     }
00843   user = strdup(decode);
00844   if (NULL == user)
00845     {
00846       free (decode);
00847       return NULL;
00848     }
00849   user[separator - decode] = '\0'; /* cut off at ':' */
00850   if (password != NULL) 
00851     {
00852       *password = strdup(separator + 1);  
00853       if (NULL == *password)
00854         {
00855 #if HAVE_MESSAGES
00856           MHD_DLOG(connection->daemon,
00857                    "Failed to allocate memory for password\n");
00858 #endif
00859           free (decode);
00860           free (user);
00861           return NULL;
00862         }
00863     }
00864   free(decode);
00865   return user;
00866 }
00867 
00868 
00876 int 
00877 MHD_queue_basic_auth_fail_response(struct MHD_Connection *connection,
00878                                    const char *realm, 
00879                                    struct MHD_Response *response) 
00880 {
00881   int ret;
00882   size_t hlen = strlen(realm) + strlen("Basic realm=\"\"") + 1;
00883   char header[hlen];
00884 
00885   snprintf(header, 
00886            sizeof (header), 
00887            "Basic realm=\"%s\"", 
00888            realm);
00889   ret = MHD_add_response_header(response,
00890                                 MHD_HTTP_HEADER_WWW_AUTHENTICATE,
00891                                 header);
00892   if (MHD_YES == ret)
00893     ret = MHD_queue_response(connection, 
00894                              MHD_HTTP_UNAUTHORIZED, 
00895                              response);
00896   return ret;
00897 }
00898 
00899 /* end of digestauth.c */

Generated on 4 Sep 2013 for GNU libmicrohttpd by  doxygen 1.6.1