Originally committed as revision 22667 to svn://svn.ffmpeg.org/ffmpeg/trunk
| ... | ... |
@@ -22,6 +22,9 @@ |
| 22 | 22 |
#include "httpauth.h" |
| 23 | 23 |
#include "libavutil/base64.h" |
| 24 | 24 |
#include "libavutil/avstring.h" |
| 25 |
+#include "internal.h" |
|
| 26 |
+#include "libavutil/random_seed.h" |
|
| 27 |
+#include "libavutil/md5.h" |
|
| 25 | 28 |
#include "avformat.h" |
| 26 | 29 |
#include <ctype.h> |
| 27 | 30 |
|
| ... | ... |
@@ -90,6 +93,53 @@ static void handle_basic_params(HTTPAuthState *state, const char *key, |
| 90 | 90 |
} |
| 91 | 91 |
} |
| 92 | 92 |
|
| 93 |
+static void handle_digest_params(HTTPAuthState *state, const char *key, |
|
| 94 |
+ int key_len, char **dest, int *dest_len) |
|
| 95 |
+{
|
|
| 96 |
+ DigestParams *digest = &state->digest_params; |
|
| 97 |
+ |
|
| 98 |
+ if (!strncmp(key, "realm=", key_len)) {
|
|
| 99 |
+ *dest = state->realm; |
|
| 100 |
+ *dest_len = sizeof(state->realm); |
|
| 101 |
+ } else if (!strncmp(key, "nonce=", key_len)) {
|
|
| 102 |
+ *dest = digest->nonce; |
|
| 103 |
+ *dest_len = sizeof(digest->nonce); |
|
| 104 |
+ } else if (!strncmp(key, "opaque=", key_len)) {
|
|
| 105 |
+ *dest = digest->opaque; |
|
| 106 |
+ *dest_len = sizeof(digest->opaque); |
|
| 107 |
+ } else if (!strncmp(key, "algorithm=", key_len)) {
|
|
| 108 |
+ *dest = digest->algorithm; |
|
| 109 |
+ *dest_len = sizeof(digest->algorithm); |
|
| 110 |
+ } else if (!strncmp(key, "qop=", key_len)) {
|
|
| 111 |
+ *dest = digest->qop; |
|
| 112 |
+ *dest_len = sizeof(digest->qop); |
|
| 113 |
+ } |
|
| 114 |
+} |
|
| 115 |
+ |
|
| 116 |
+static void handle_digest_update(HTTPAuthState *state, const char *key, |
|
| 117 |
+ int key_len, char **dest, int *dest_len) |
|
| 118 |
+{
|
|
| 119 |
+ DigestParams *digest = &state->digest_params; |
|
| 120 |
+ |
|
| 121 |
+ if (!strncmp(key, "nextnonce=", key_len)) {
|
|
| 122 |
+ *dest = digest->nonce; |
|
| 123 |
+ *dest_len = sizeof(digest->nonce); |
|
| 124 |
+ } |
|
| 125 |
+} |
|
| 126 |
+ |
|
| 127 |
+static void choose_qop(char *qop, int size) |
|
| 128 |
+{
|
|
| 129 |
+ char *ptr = strstr(qop, "auth"); |
|
| 130 |
+ char *end = ptr + strlen("auth");
|
|
| 131 |
+ |
|
| 132 |
+ if (ptr && (!*end || isspace(*end) || *end == ',') && |
|
| 133 |
+ (ptr == qop || isspace(ptr[-1]) || ptr[-1] == ',')) {
|
|
| 134 |
+ av_strlcpy(qop, "auth", size); |
|
| 135 |
+ } else {
|
|
| 136 |
+ qop[0] = 0; |
|
| 137 |
+ } |
|
| 138 |
+} |
|
| 139 |
+ |
|
| 93 | 140 |
void ff_http_auth_handle_header(HTTPAuthState *state, const char *key, |
| 94 | 141 |
const char *value) |
| 95 | 142 |
{
|
| ... | ... |
@@ -103,8 +153,139 @@ void ff_http_auth_handle_header(HTTPAuthState *state, const char *key, |
| 103 | 103 |
state->auth_type = HTTP_AUTH_BASIC; |
| 104 | 104 |
state->realm[0] = 0; |
| 105 | 105 |
parse_key_value(p, handle_basic_params, state); |
| 106 |
+ } else if (av_stristart(value, "Digest ", &p) && |
|
| 107 |
+ state->auth_type <= HTTP_AUTH_DIGEST) {
|
|
| 108 |
+ state->auth_type = HTTP_AUTH_DIGEST; |
|
| 109 |
+ memset(&state->digest_params, 0, sizeof(DigestParams)); |
|
| 110 |
+ state->realm[0] = 0; |
|
| 111 |
+ parse_key_value(p, handle_digest_params, state); |
|
| 112 |
+ choose_qop(state->digest_params.qop, |
|
| 113 |
+ sizeof(state->digest_params.qop)); |
|
| 106 | 114 |
} |
| 115 |
+ } else if (!strcmp(key, "Authentication-Info")) {
|
|
| 116 |
+ parse_key_value(value, handle_digest_update, state); |
|
| 117 |
+ } |
|
| 118 |
+} |
|
| 119 |
+ |
|
| 120 |
+ |
|
| 121 |
+static void update_md5_strings(struct AVMD5 *md5ctx, ...) |
|
| 122 |
+{
|
|
| 123 |
+ va_list vl; |
|
| 124 |
+ |
|
| 125 |
+ va_start(vl, md5ctx); |
|
| 126 |
+ while (1) {
|
|
| 127 |
+ const char* str = va_arg(vl, const char*); |
|
| 128 |
+ if (!str) |
|
| 129 |
+ break; |
|
| 130 |
+ av_md5_update(md5ctx, str, strlen(str)); |
|
| 107 | 131 |
} |
| 132 |
+ va_end(vl); |
|
| 133 |
+} |
|
| 134 |
+ |
|
| 135 |
+/* Generate a digest reply, according to RFC 2617. */ |
|
| 136 |
+static char *make_digest_auth(HTTPAuthState *state, const char *username, |
|
| 137 |
+ const char *password, const char *uri, |
|
| 138 |
+ const char *method) |
|
| 139 |
+{
|
|
| 140 |
+ DigestParams *digest = &state->digest_params; |
|
| 141 |
+ int len; |
|
| 142 |
+ uint32_t cnonce_buf[2]; |
|
| 143 |
+ char cnonce[9]; |
|
| 144 |
+ char nc[9]; |
|
| 145 |
+ int i; |
|
| 146 |
+ char A1hash[33], A2hash[33], response[33]; |
|
| 147 |
+ struct AVMD5 *md5ctx; |
|
| 148 |
+ uint8_t hash[16]; |
|
| 149 |
+ char *authstr; |
|
| 150 |
+ |
|
| 151 |
+ digest->nc++; |
|
| 152 |
+ snprintf(nc, sizeof(nc), "%08x", digest->nc); |
|
| 153 |
+ |
|
| 154 |
+ /* Generate a client nonce. */ |
|
| 155 |
+ for (i = 0; i < 2; i++) |
|
| 156 |
+ cnonce_buf[i] = ff_random_get_seed(); |
|
| 157 |
+ ff_data_to_hex(cnonce, (const uint8_t*) cnonce_buf, sizeof(cnonce_buf), 1); |
|
| 158 |
+ cnonce[2*sizeof(cnonce_buf)] = 0; |
|
| 159 |
+ |
|
| 160 |
+ md5ctx = av_malloc(av_md5_size); |
|
| 161 |
+ if (!md5ctx) |
|
| 162 |
+ return NULL; |
|
| 163 |
+ |
|
| 164 |
+ av_md5_init(md5ctx); |
|
| 165 |
+ update_md5_strings(md5ctx, username, ":", state->realm, ":", password, NULL); |
|
| 166 |
+ av_md5_final(md5ctx, hash); |
|
| 167 |
+ ff_data_to_hex(A1hash, hash, 16, 1); |
|
| 168 |
+ A1hash[32] = 0; |
|
| 169 |
+ |
|
| 170 |
+ if (!strcmp(digest->algorithm, "") || !strcmp(digest->algorithm, "MD5")) {
|
|
| 171 |
+ } else if (!strcmp(digest->algorithm, "MD5-sess")) {
|
|
| 172 |
+ av_md5_init(md5ctx); |
|
| 173 |
+ update_md5_strings(md5ctx, A1hash, ":", digest->nonce, ":", cnonce, NULL); |
|
| 174 |
+ av_md5_final(md5ctx, hash); |
|
| 175 |
+ ff_data_to_hex(A1hash, hash, 16, 1); |
|
| 176 |
+ A1hash[32] = 0; |
|
| 177 |
+ } else {
|
|
| 178 |
+ /* Unsupported algorithm */ |
|
| 179 |
+ av_free(md5ctx); |
|
| 180 |
+ return NULL; |
|
| 181 |
+ } |
|
| 182 |
+ |
|
| 183 |
+ av_md5_init(md5ctx); |
|
| 184 |
+ update_md5_strings(md5ctx, method, ":", uri, NULL); |
|
| 185 |
+ av_md5_final(md5ctx, hash); |
|
| 186 |
+ ff_data_to_hex(A2hash, hash, 16, 1); |
|
| 187 |
+ A2hash[32] = 0; |
|
| 188 |
+ |
|
| 189 |
+ av_md5_init(md5ctx); |
|
| 190 |
+ update_md5_strings(md5ctx, A1hash, ":", digest->nonce, NULL); |
|
| 191 |
+ if (!strcmp(digest->qop, "auth") || !strcmp(digest->qop, "auth-int")) {
|
|
| 192 |
+ update_md5_strings(md5ctx, ":", nc, ":", cnonce, ":", digest->qop, NULL); |
|
| 193 |
+ } |
|
| 194 |
+ update_md5_strings(md5ctx, ":", A2hash, NULL); |
|
| 195 |
+ av_md5_final(md5ctx, hash); |
|
| 196 |
+ ff_data_to_hex(response, hash, 16, 1); |
|
| 197 |
+ response[32] = 0; |
|
| 198 |
+ |
|
| 199 |
+ av_free(md5ctx); |
|
| 200 |
+ |
|
| 201 |
+ if (!strcmp(digest->qop, "") || !strcmp(digest->qop, "auth")) {
|
|
| 202 |
+ } else if (!strcmp(digest->qop, "auth-int")) {
|
|
| 203 |
+ /* qop=auth-int not supported */ |
|
| 204 |
+ return NULL; |
|
| 205 |
+ } else {
|
|
| 206 |
+ /* Unsupported qop value. */ |
|
| 207 |
+ return NULL; |
|
| 208 |
+ } |
|
| 209 |
+ |
|
| 210 |
+ len = strlen(username) + strlen(state->realm) + strlen(digest->nonce) + |
|
| 211 |
+ strlen(uri) + strlen(response) + strlen(digest->algorithm) + |
|
| 212 |
+ strlen(digest->opaque) + strlen(digest->qop) + strlen(cnonce) + |
|
| 213 |
+ strlen(nc) + 150; |
|
| 214 |
+ |
|
| 215 |
+ authstr = av_malloc(len); |
|
| 216 |
+ if (!authstr) |
|
| 217 |
+ return NULL; |
|
| 218 |
+ snprintf(authstr, len, "Authorization: Digest "); |
|
| 219 |
+ |
|
| 220 |
+ /* TODO: Escape the quoted strings properly. */ |
|
| 221 |
+ av_strlcatf(authstr, len, "username=\"%s\"", username); |
|
| 222 |
+ av_strlcatf(authstr, len, ",realm=\"%s\"", state->realm); |
|
| 223 |
+ av_strlcatf(authstr, len, ",nonce=\"%s\"", digest->nonce); |
|
| 224 |
+ av_strlcatf(authstr, len, ",uri=\"%s\"", uri); |
|
| 225 |
+ av_strlcatf(authstr, len, ",response=\"%s\"", response); |
|
| 226 |
+ if (digest->algorithm[0]) |
|
| 227 |
+ av_strlcatf(authstr, len, ",algorithm=%s", digest->algorithm); |
|
| 228 |
+ if (digest->opaque[0]) |
|
| 229 |
+ av_strlcatf(authstr, len, ",opaque=\"%s\"", digest->opaque); |
|
| 230 |
+ if (digest->qop[0]) {
|
|
| 231 |
+ av_strlcatf(authstr, len, ",qop=\"%s\"", digest->qop); |
|
| 232 |
+ av_strlcatf(authstr, len, ",cnonce=\"%s\"", cnonce); |
|
| 233 |
+ av_strlcatf(authstr, len, ",nc=%s", nc); |
|
| 234 |
+ } |
|
| 235 |
+ |
|
| 236 |
+ av_strlcatf(authstr, len, "\r\n"); |
|
| 237 |
+ |
|
| 238 |
+ return authstr; |
|
| 108 | 239 |
} |
| 109 | 240 |
|
| 110 | 241 |
char *ff_http_auth_create_response(HTTPAuthState *state, const char *auth, |
| ... | ... |
@@ -126,6 +307,17 @@ char *ff_http_auth_create_response(HTTPAuthState *state, const char *auth, |
| 126 | 126 |
ptr = authstr + strlen(authstr); |
| 127 | 127 |
av_base64_encode(ptr, auth_b64_len, auth, strlen(auth)); |
| 128 | 128 |
av_strlcat(ptr, "\r\n", len); |
| 129 |
+ } else if (state->auth_type == HTTP_AUTH_DIGEST) {
|
|
| 130 |
+ char *username = av_strdup(auth), *password; |
|
| 131 |
+ |
|
| 132 |
+ if (!username) |
|
| 133 |
+ return NULL; |
|
| 134 |
+ |
|
| 135 |
+ if ((password = strchr(username, ':'))) {
|
|
| 136 |
+ *password++ = 0; |
|
| 137 |
+ authstr = make_digest_auth(state, username, password, path, method); |
|
| 138 |
+ } |
|
| 139 |
+ av_free(username); |
|
| 129 | 140 |
} |
| 130 | 141 |
return authstr; |
| 131 | 142 |
} |
| ... | ... |
@@ -29,8 +29,22 @@ typedef enum HTTPAuthType {
|
| 29 | 29 |
HTTP_AUTH_NONE = 0, /**< No authentication specified */ |
| 30 | 30 |
HTTP_AUTH_BASIC, /**< HTTP 1.0 Basic auth from RFC 1945 |
| 31 | 31 |
* (also in RFC 2617) */ |
| 32 |
+ HTTP_AUTH_DIGEST, /**< HTTP 1.1 Digest auth from RFC 2617 */ |
|
| 32 | 33 |
} HTTPAuthType; |
| 33 | 34 |
|
| 35 |
+typedef struct {
|
|
| 36 |
+ char nonce[300]; /**< Server specified nonce */ |
|
| 37 |
+ char algorithm[10]; /**< Server specified digest algorithm */ |
|
| 38 |
+ char qop[30]; /**< Quality of protection, containing the one |
|
| 39 |
+ * that we've chosen to use, from the |
|
| 40 |
+ * alternatives that the server offered. */ |
|
| 41 |
+ char opaque[300]; /**< A server-specified string that should be |
|
| 42 |
+ * included in authentication responses, not |
|
| 43 |
+ * included in the actual digest calculation. */ |
|
| 44 |
+ int nc; /**< Nonce count, the number of earlier replies |
|
| 45 |
+ * where this particular nonce has been used. */ |
|
| 46 |
+} DigestParams; |
|
| 47 |
+ |
|
| 34 | 48 |
/** |
| 35 | 49 |
* HTTP Authentication state structure. Must be zero-initialized |
| 36 | 50 |
* before used with the functions below. |
| ... | ... |
@@ -44,6 +58,10 @@ typedef struct {
|
| 44 | 44 |
* Authentication realm |
| 45 | 45 |
*/ |
| 46 | 46 |
char realm[200]; |
| 47 |
+ /** |
|
| 48 |
+ * The parameters specifiec to digest authentication. |
|
| 49 |
+ */ |
|
| 50 |
+ DigestParams digest_params; |
|
| 47 | 51 |
} HTTPAuthState; |
| 48 | 52 |
|
| 49 | 53 |
void ff_http_auth_handle_header(HTTPAuthState *state, const char *key, |