• Main Page
  • Related Pages
  • Modules
  • Data Structures
  • Files
  • File List
  • Globals

libavformat/httpauth.c

Go to the documentation of this file.
00001 /*
00002  * HTTP authentication
00003  * Copyright (c) 2010 Martin Storsjo
00004  *
00005  * This file is part of FFmpeg.
00006  *
00007  * FFmpeg is free software; you can redistribute it and/or
00008  * modify it under the terms of the GNU Lesser General Public
00009  * License as published by the Free Software Foundation; either
00010  * version 2.1 of the License, or (at your option) any later version.
00011  *
00012  * FFmpeg is distributed in the hope that it will be useful,
00013  * but WITHOUT ANY WARRANTY; without even the implied warranty of
00014  * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the GNU
00015  * Lesser General Public License for more details.
00016  *
00017  * You should have received a copy of the GNU Lesser General Public
00018  * License along with FFmpeg; if not, write to the Free Software
00019  * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA
00020  */
00021 
00022 #include "httpauth.h"
00023 #include "libavutil/base64.h"
00024 #include "libavutil/avstring.h"
00025 #include "internal.h"
00026 #include "libavutil/random_seed.h"
00027 #include "libavutil/md5.h"
00028 #include "avformat.h"
00029 #include <ctype.h>
00030 
00031 static void parse_key_value(const char *params,
00032                             void (*callback_get_buf)(HTTPAuthState *state,
00033                             const char *key, int key_len,
00034                             char **dest, int *dest_len), HTTPAuthState *state)
00035 {
00036     const char *ptr = params;
00037 
00038     /* Parse key=value pairs. */
00039     for (;;) {
00040         const char *key;
00041         char *dest = NULL, *dest_end;
00042         int key_len, dest_len = 0;
00043 
00044         /* Skip whitespace and potential commas. */
00045         while (*ptr && (isspace(*ptr) || *ptr == ','))
00046             ptr++;
00047         if (!*ptr)
00048             break;
00049 
00050         key = ptr;
00051 
00052         if (!(ptr = strchr(key, '=')))
00053             break;
00054         ptr++;
00055         key_len = ptr - key;
00056 
00057         callback_get_buf(state, key, key_len, &dest, &dest_len);
00058         dest_end = dest + dest_len - 1;
00059 
00060         if (*ptr == '\"') {
00061             ptr++;
00062             while (*ptr && *ptr != '\"') {
00063                 if (*ptr == '\\') {
00064                     if (!ptr[1])
00065                         break;
00066                     if (dest && dest < dest_end)
00067                         *dest++ = ptr[1];
00068                     ptr += 2;
00069                 } else {
00070                     if (dest && dest < dest_end)
00071                         *dest++ = *ptr;
00072                     ptr++;
00073                 }
00074             }
00075             if (*ptr == '\"')
00076                 ptr++;
00077         } else {
00078             for (; *ptr && !(isspace(*ptr) || *ptr == ','); ptr++)
00079                 if (dest && dest < dest_end)
00080                     *dest++ = *ptr;
00081         }
00082         if (dest)
00083             *dest = 0;
00084     }
00085 }
00086 
00087 static void handle_basic_params(HTTPAuthState *state, const char *key,
00088                                 int key_len, char **dest, int *dest_len)
00089 {
00090     if (!strncmp(key, "realm=", key_len)) {
00091         *dest     =        state->realm;
00092         *dest_len = sizeof(state->realm);
00093     }
00094 }
00095 
00096 static void handle_digest_params(HTTPAuthState *state, const char *key,
00097                                  int key_len, char **dest, int *dest_len)
00098 {
00099     DigestParams *digest = &state->digest_params;
00100 
00101     if (!strncmp(key, "realm=", key_len)) {
00102         *dest     =        state->realm;
00103         *dest_len = sizeof(state->realm);
00104     } else if (!strncmp(key, "nonce=", key_len)) {
00105         *dest     =        digest->nonce;
00106         *dest_len = sizeof(digest->nonce);
00107     } else if (!strncmp(key, "opaque=", key_len)) {
00108         *dest     =        digest->opaque;
00109         *dest_len = sizeof(digest->opaque);
00110     } else if (!strncmp(key, "algorithm=", key_len)) {
00111         *dest     =        digest->algorithm;
00112         *dest_len = sizeof(digest->algorithm);
00113     } else if (!strncmp(key, "qop=", key_len)) {
00114         *dest     =        digest->qop;
00115         *dest_len = sizeof(digest->qop);
00116     }
00117 }
00118 
00119 static void handle_digest_update(HTTPAuthState *state, const char *key,
00120                                  int key_len, char **dest, int *dest_len)
00121 {
00122     DigestParams *digest = &state->digest_params;
00123 
00124     if (!strncmp(key, "nextnonce=", key_len)) {
00125         *dest     =        digest->nonce;
00126         *dest_len = sizeof(digest->nonce);
00127     }
00128 }
00129 
00130 static void choose_qop(char *qop, int size)
00131 {
00132     char *ptr = strstr(qop, "auth");
00133     char *end = ptr + strlen("auth");
00134 
00135     if (ptr && (!*end || isspace(*end) || *end == ',') &&
00136         (ptr == qop || isspace(ptr[-1]) || ptr[-1] == ',')) {
00137         av_strlcpy(qop, "auth", size);
00138     } else {
00139         qop[0] = 0;
00140     }
00141 }
00142 
00143 void ff_http_auth_handle_header(HTTPAuthState *state, const char *key,
00144                                 const char *value)
00145 {
00146     if (!strcmp(key, "WWW-Authenticate")) {
00147         const char *p;
00148         if (av_stristart(value, "Basic ", &p) &&
00149             state->auth_type <= HTTP_AUTH_BASIC) {
00150             state->auth_type = HTTP_AUTH_BASIC;
00151             state->realm[0] = 0;
00152             parse_key_value(p, handle_basic_params, state);
00153         } else if (av_stristart(value, "Digest ", &p) &&
00154                    state->auth_type <= HTTP_AUTH_DIGEST) {
00155             state->auth_type = HTTP_AUTH_DIGEST;
00156             memset(&state->digest_params, 0, sizeof(DigestParams));
00157             state->realm[0] = 0;
00158             parse_key_value(p, handle_digest_params, state);
00159             choose_qop(state->digest_params.qop,
00160                        sizeof(state->digest_params.qop));
00161         }
00162     } else if (!strcmp(key, "Authentication-Info")) {
00163         parse_key_value(value, handle_digest_update, state);
00164     }
00165 }
00166 
00167 
00168 static void update_md5_strings(struct AVMD5 *md5ctx, ...)
00169 {
00170     va_list vl;
00171 
00172     va_start(vl, md5ctx);
00173     while (1) {
00174         const char* str = va_arg(vl, const char*);
00175         if (!str)
00176             break;
00177         av_md5_update(md5ctx, str, strlen(str));
00178     }
00179     va_end(vl);
00180 }
00181 
00182 /* Generate a digest reply, according to RFC 2617. */
00183 static char *make_digest_auth(HTTPAuthState *state, const char *username,
00184                               const char *password, const char *uri,
00185                               const char *method)
00186 {
00187     DigestParams *digest = &state->digest_params;
00188     int len;
00189     uint32_t cnonce_buf[2];
00190     char cnonce[17];
00191     char nc[9];
00192     int i;
00193     char A1hash[33], A2hash[33], response[33];
00194     struct AVMD5 *md5ctx;
00195     uint8_t hash[16];
00196     char *authstr;
00197 
00198     digest->nc++;
00199     snprintf(nc, sizeof(nc), "%08x", digest->nc);
00200 
00201     /* Generate a client nonce. */
00202     for (i = 0; i < 2; i++)
00203         cnonce_buf[i] = ff_random_get_seed();
00204     ff_data_to_hex(cnonce, (const uint8_t*) cnonce_buf, sizeof(cnonce_buf), 1);
00205     cnonce[2*sizeof(cnonce_buf)] = 0;
00206 
00207     md5ctx = av_malloc(av_md5_size);
00208     if (!md5ctx)
00209         return NULL;
00210 
00211     av_md5_init(md5ctx);
00212     update_md5_strings(md5ctx, username, ":", state->realm, ":", password, NULL);
00213     av_md5_final(md5ctx, hash);
00214     ff_data_to_hex(A1hash, hash, 16, 1);
00215     A1hash[32] = 0;
00216 
00217     if (!strcmp(digest->algorithm, "") || !strcmp(digest->algorithm, "MD5")) {
00218     } else if (!strcmp(digest->algorithm, "MD5-sess")) {
00219         av_md5_init(md5ctx);
00220         update_md5_strings(md5ctx, A1hash, ":", digest->nonce, ":", cnonce, NULL);
00221         av_md5_final(md5ctx, hash);
00222         ff_data_to_hex(A1hash, hash, 16, 1);
00223         A1hash[32] = 0;
00224     } else {
00225         /* Unsupported algorithm */
00226         av_free(md5ctx);
00227         return NULL;
00228     }
00229 
00230     av_md5_init(md5ctx);
00231     update_md5_strings(md5ctx, method, ":", uri, NULL);
00232     av_md5_final(md5ctx, hash);
00233     ff_data_to_hex(A2hash, hash, 16, 1);
00234     A2hash[32] = 0;
00235 
00236     av_md5_init(md5ctx);
00237     update_md5_strings(md5ctx, A1hash, ":", digest->nonce, NULL);
00238     if (!strcmp(digest->qop, "auth") || !strcmp(digest->qop, "auth-int")) {
00239         update_md5_strings(md5ctx, ":", nc, ":", cnonce, ":", digest->qop, NULL);
00240     }
00241     update_md5_strings(md5ctx, ":", A2hash, NULL);
00242     av_md5_final(md5ctx, hash);
00243     ff_data_to_hex(response, hash, 16, 1);
00244     response[32] = 0;
00245 
00246     av_free(md5ctx);
00247 
00248     if (!strcmp(digest->qop, "") || !strcmp(digest->qop, "auth")) {
00249     } else if (!strcmp(digest->qop, "auth-int")) {
00250         /* qop=auth-int not supported */
00251         return NULL;
00252     } else {
00253         /* Unsupported qop value. */
00254         return NULL;
00255     }
00256 
00257     len = strlen(username) + strlen(state->realm) + strlen(digest->nonce) +
00258               strlen(uri) + strlen(response) + strlen(digest->algorithm) +
00259               strlen(digest->opaque) + strlen(digest->qop) + strlen(cnonce) +
00260               strlen(nc) + 150;
00261 
00262     authstr = av_malloc(len);
00263     if (!authstr)
00264         return NULL;
00265     snprintf(authstr, len, "Authorization: Digest ");
00266 
00267     /* TODO: Escape the quoted strings properly. */
00268     av_strlcatf(authstr, len, "username=\"%s\"",   username);
00269     av_strlcatf(authstr, len, ",realm=\"%s\"",     state->realm);
00270     av_strlcatf(authstr, len, ",nonce=\"%s\"",     digest->nonce);
00271     av_strlcatf(authstr, len, ",uri=\"%s\"",       uri);
00272     av_strlcatf(authstr, len, ",response=\"%s\"",  response);
00273     if (digest->algorithm[0])
00274         av_strlcatf(authstr, len, ",algorithm=%s",  digest->algorithm);
00275     if (digest->opaque[0])
00276         av_strlcatf(authstr, len, ",opaque=\"%s\"", digest->opaque);
00277     if (digest->qop[0]) {
00278         av_strlcatf(authstr, len, ",qop=\"%s\"",    digest->qop);
00279         av_strlcatf(authstr, len, ",cnonce=\"%s\"", cnonce);
00280         av_strlcatf(authstr, len, ",nc=%s",         nc);
00281     }
00282 
00283     av_strlcatf(authstr, len, "\r\n");
00284 
00285     return authstr;
00286 }
00287 
00288 char *ff_http_auth_create_response(HTTPAuthState *state, const char *auth,
00289                                    const char *path, const char *method)
00290 {
00291     char *authstr = NULL;
00292 
00293     if (!auth || !strchr(auth, ':'))
00294         return NULL;
00295 
00296     if (state->auth_type == HTTP_AUTH_BASIC) {
00297         int auth_b64_len = (strlen(auth) + 2) / 3 * 4 + 1;
00298         int len = auth_b64_len + 30;
00299         char *ptr;
00300         authstr = av_malloc(len);
00301         if (!authstr)
00302             return NULL;
00303         snprintf(authstr, len, "Authorization: Basic ");
00304         ptr = authstr + strlen(authstr);
00305         av_base64_encode(ptr, auth_b64_len, auth, strlen(auth));
00306         av_strlcat(ptr, "\r\n", len);
00307     } else if (state->auth_type == HTTP_AUTH_DIGEST) {
00308         char *username = av_strdup(auth), *password;
00309 
00310         if (!username)
00311             return NULL;
00312 
00313         if ((password = strchr(username, ':'))) {
00314             *password++ = 0;
00315             authstr = make_digest_auth(state, username, password, path, method);
00316         }
00317         av_free(username);
00318     }
00319     return authstr;
00320 }
00321 

Generated on Fri Sep 16 2011 17:17:48 for FFmpeg by  doxygen 1.7.1