Originally committed as revision 22667 to svn://svn.ffmpeg.org/ffmpeg/trunk
Martin Storsjö authored on 2010/03/25 22:58:26... | ... |
@@ -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, |