00001
00002
00003
00004
00005
00006
00007
00008
00009
00010
00011
00012
00013
00014
00015
00016
00017
00018
00019
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
00039 for (;;) {
00040 const char *key;
00041 char *dest = NULL, *dest_end;
00042 int key_len, dest_len = 0;
00043
00044
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
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
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
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
00251 return NULL;
00252 } else {
00253
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
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