Libav 0.7.1
|
00001 /* 00002 * HTTP authentication 00003 * Copyright (c) 2010 Martin Storsjo 00004 * 00005 * This file is part of Libav. 00006 * 00007 * Libav 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 * Libav 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 Libav; 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 handle_basic_params(HTTPAuthState *state, const char *key, 00032 int key_len, char **dest, int *dest_len) 00033 { 00034 if (!strncmp(key, "realm=", key_len)) { 00035 *dest = state->realm; 00036 *dest_len = sizeof(state->realm); 00037 } 00038 } 00039 00040 static void handle_digest_params(HTTPAuthState *state, const char *key, 00041 int key_len, char **dest, int *dest_len) 00042 { 00043 DigestParams *digest = &state->digest_params; 00044 00045 if (!strncmp(key, "realm=", key_len)) { 00046 *dest = state->realm; 00047 *dest_len = sizeof(state->realm); 00048 } else if (!strncmp(key, "nonce=", key_len)) { 00049 *dest = digest->nonce; 00050 *dest_len = sizeof(digest->nonce); 00051 } else if (!strncmp(key, "opaque=", key_len)) { 00052 *dest = digest->opaque; 00053 *dest_len = sizeof(digest->opaque); 00054 } else if (!strncmp(key, "algorithm=", key_len)) { 00055 *dest = digest->algorithm; 00056 *dest_len = sizeof(digest->algorithm); 00057 } else if (!strncmp(key, "qop=", key_len)) { 00058 *dest = digest->qop; 00059 *dest_len = sizeof(digest->qop); 00060 } 00061 } 00062 00063 static void handle_digest_update(HTTPAuthState *state, const char *key, 00064 int key_len, char **dest, int *dest_len) 00065 { 00066 DigestParams *digest = &state->digest_params; 00067 00068 if (!strncmp(key, "nextnonce=", key_len)) { 00069 *dest = digest->nonce; 00070 *dest_len = sizeof(digest->nonce); 00071 } 00072 } 00073 00074 static void choose_qop(char *qop, int size) 00075 { 00076 char *ptr = strstr(qop, "auth"); 00077 char *end = ptr + strlen("auth"); 00078 00079 if (ptr && (!*end || isspace(*end) || *end == ',') && 00080 (ptr == qop || isspace(ptr[-1]) || ptr[-1] == ',')) { 00081 av_strlcpy(qop, "auth", size); 00082 } else { 00083 qop[0] = 0; 00084 } 00085 } 00086 00087 void ff_http_auth_handle_header(HTTPAuthState *state, const char *key, 00088 const char *value) 00089 { 00090 if (!strcmp(key, "WWW-Authenticate")) { 00091 const char *p; 00092 if (av_stristart(value, "Basic ", &p) && 00093 state->auth_type <= HTTP_AUTH_BASIC) { 00094 state->auth_type = HTTP_AUTH_BASIC; 00095 state->realm[0] = 0; 00096 ff_parse_key_value(p, (ff_parse_key_val_cb) handle_basic_params, 00097 state); 00098 } else if (av_stristart(value, "Digest ", &p) && 00099 state->auth_type <= HTTP_AUTH_DIGEST) { 00100 state->auth_type = HTTP_AUTH_DIGEST; 00101 memset(&state->digest_params, 0, sizeof(DigestParams)); 00102 state->realm[0] = 0; 00103 ff_parse_key_value(p, (ff_parse_key_val_cb) handle_digest_params, 00104 state); 00105 choose_qop(state->digest_params.qop, 00106 sizeof(state->digest_params.qop)); 00107 } 00108 } else if (!strcmp(key, "Authentication-Info")) { 00109 ff_parse_key_value(value, (ff_parse_key_val_cb) handle_digest_update, 00110 state); 00111 } 00112 } 00113 00114 00115 static void update_md5_strings(struct AVMD5 *md5ctx, ...) 00116 { 00117 va_list vl; 00118 00119 va_start(vl, md5ctx); 00120 while (1) { 00121 const char* str = va_arg(vl, const char*); 00122 if (!str) 00123 break; 00124 av_md5_update(md5ctx, str, strlen(str)); 00125 } 00126 va_end(vl); 00127 } 00128 00129 /* Generate a digest reply, according to RFC 2617. */ 00130 static char *make_digest_auth(HTTPAuthState *state, const char *username, 00131 const char *password, const char *uri, 00132 const char *method) 00133 { 00134 DigestParams *digest = &state->digest_params; 00135 int len; 00136 uint32_t cnonce_buf[2]; 00137 char cnonce[17]; 00138 char nc[9]; 00139 int i; 00140 char A1hash[33], A2hash[33], response[33]; 00141 struct AVMD5 *md5ctx; 00142 uint8_t hash[16]; 00143 char *authstr; 00144 00145 digest->nc++; 00146 snprintf(nc, sizeof(nc), "%08x", digest->nc); 00147 00148 /* Generate a client nonce. */ 00149 for (i = 0; i < 2; i++) 00150 cnonce_buf[i] = av_get_random_seed(); 00151 ff_data_to_hex(cnonce, (const uint8_t*) cnonce_buf, sizeof(cnonce_buf), 1); 00152 cnonce[2*sizeof(cnonce_buf)] = 0; 00153 00154 md5ctx = av_malloc(av_md5_size); 00155 if (!md5ctx) 00156 return NULL; 00157 00158 av_md5_init(md5ctx); 00159 update_md5_strings(md5ctx, username, ":", state->realm, ":", password, NULL); 00160 av_md5_final(md5ctx, hash); 00161 ff_data_to_hex(A1hash, hash, 16, 1); 00162 A1hash[32] = 0; 00163 00164 if (!strcmp(digest->algorithm, "") || !strcmp(digest->algorithm, "MD5")) { 00165 } else if (!strcmp(digest->algorithm, "MD5-sess")) { 00166 av_md5_init(md5ctx); 00167 update_md5_strings(md5ctx, A1hash, ":", digest->nonce, ":", cnonce, NULL); 00168 av_md5_final(md5ctx, hash); 00169 ff_data_to_hex(A1hash, hash, 16, 1); 00170 A1hash[32] = 0; 00171 } else { 00172 /* Unsupported algorithm */ 00173 av_free(md5ctx); 00174 return NULL; 00175 } 00176 00177 av_md5_init(md5ctx); 00178 update_md5_strings(md5ctx, method, ":", uri, NULL); 00179 av_md5_final(md5ctx, hash); 00180 ff_data_to_hex(A2hash, hash, 16, 1); 00181 A2hash[32] = 0; 00182 00183 av_md5_init(md5ctx); 00184 update_md5_strings(md5ctx, A1hash, ":", digest->nonce, NULL); 00185 if (!strcmp(digest->qop, "auth") || !strcmp(digest->qop, "auth-int")) { 00186 update_md5_strings(md5ctx, ":", nc, ":", cnonce, ":", digest->qop, NULL); 00187 } 00188 update_md5_strings(md5ctx, ":", A2hash, NULL); 00189 av_md5_final(md5ctx, hash); 00190 ff_data_to_hex(response, hash, 16, 1); 00191 response[32] = 0; 00192 00193 av_free(md5ctx); 00194 00195 if (!strcmp(digest->qop, "") || !strcmp(digest->qop, "auth")) { 00196 } else if (!strcmp(digest->qop, "auth-int")) { 00197 /* qop=auth-int not supported */ 00198 return NULL; 00199 } else { 00200 /* Unsupported qop value. */ 00201 return NULL; 00202 } 00203 00204 len = strlen(username) + strlen(state->realm) + strlen(digest->nonce) + 00205 strlen(uri) + strlen(response) + strlen(digest->algorithm) + 00206 strlen(digest->opaque) + strlen(digest->qop) + strlen(cnonce) + 00207 strlen(nc) + 150; 00208 00209 authstr = av_malloc(len); 00210 if (!authstr) 00211 return NULL; 00212 snprintf(authstr, len, "Authorization: Digest "); 00213 00214 /* TODO: Escape the quoted strings properly. */ 00215 av_strlcatf(authstr, len, "username=\"%s\"", username); 00216 av_strlcatf(authstr, len, ",realm=\"%s\"", state->realm); 00217 av_strlcatf(authstr, len, ",nonce=\"%s\"", digest->nonce); 00218 av_strlcatf(authstr, len, ",uri=\"%s\"", uri); 00219 av_strlcatf(authstr, len, ",response=\"%s\"", response); 00220 if (digest->algorithm[0]) 00221 av_strlcatf(authstr, len, ",algorithm=%s", digest->algorithm); 00222 if (digest->opaque[0]) 00223 av_strlcatf(authstr, len, ",opaque=\"%s\"", digest->opaque); 00224 if (digest->qop[0]) { 00225 av_strlcatf(authstr, len, ",qop=\"%s\"", digest->qop); 00226 av_strlcatf(authstr, len, ",cnonce=\"%s\"", cnonce); 00227 av_strlcatf(authstr, len, ",nc=%s", nc); 00228 } 00229 00230 av_strlcatf(authstr, len, "\r\n"); 00231 00232 return authstr; 00233 } 00234 00235 char *ff_http_auth_create_response(HTTPAuthState *state, const char *auth, 00236 const char *path, const char *method) 00237 { 00238 char *authstr = NULL; 00239 00240 if (!auth || !strchr(auth, ':')) 00241 return NULL; 00242 00243 if (state->auth_type == HTTP_AUTH_BASIC) { 00244 int auth_b64_len = AV_BASE64_SIZE(strlen(auth)); 00245 int len = auth_b64_len + 30; 00246 char *ptr; 00247 authstr = av_malloc(len); 00248 if (!authstr) 00249 return NULL; 00250 snprintf(authstr, len, "Authorization: Basic "); 00251 ptr = authstr + strlen(authstr); 00252 av_base64_encode(ptr, auth_b64_len, auth, strlen(auth)); 00253 av_strlcat(ptr, "\r\n", len - (ptr - authstr)); 00254 } else if (state->auth_type == HTTP_AUTH_DIGEST) { 00255 char *username = av_strdup(auth), *password; 00256 00257 if (!username) 00258 return NULL; 00259 00260 if ((password = strchr(username, ':'))) { 00261 *password++ = 0; 00262 authstr = make_digest_auth(state, username, password, path, method); 00263 } 00264 av_free(username); 00265 } 00266 return authstr; 00267 } 00268