~ubuntu-branches/ubuntu/jaunty/transmission/jaunty-updates

« back to all changes in this revision

Viewing changes to third-party/shttpd/auth.c

  • Committer: Bazaar Package Importer
  • Author(s): Chris Coulson
  • Date: 2008-11-28 15:33:48 UTC
  • mfrom: (1.1.19 upstream)
  • Revision ID: james.westby@ubuntu.com-20081128153348-it70trfnxiroblmc
Tags: 1.40-0ubuntu1
* New upstream release (LP: #302672)
  - Tracker communication uses fewer resources
  - More accurate bandwidth limits
  - Reduce disk fragmentation by preallocating files (LP: #287726)
  - Stability, security and performance improvements to the RPC /
    Web UI server (closes LP: #290423)
  - Support compression when serving Web UI and RPC responses
  - Simplify the RPC whitelist
  - Fix bug that prevented handshakes with encrypted BitComet peers
  - Fix 1.3x bug that could re-download some data unnecessarily
    (LP: #295040)
  - Option to automatically update the blocklist weekly
  - Added off-hour bandwidth scheduling
  - Simplify file/priority selection in the details dialog
  - Fix a couple of crashes
  - New / updated translations
  - Don't inhibit hibernation by default (LP: #292929)
  - Use "close" animation when sending to notification area (LP: #130811)
  - Fix resize problems (LP: #269872)
  - Support "--version" option when launching from command line
    (LP: #292011)
  - Correctly parse announce URLs that have leading or trailing
    spaces (LP: #262411)
  - Display an error when "Open Torrent" fails (LP: #281463)
* Dropped 10_fix_crasher_from_upstream.dpatch: Fix is in this
  upstream release.
* debian/control: Don't just build-depend on libcurl-dev, which is
  a virtual package.

Show diffs side-by-side

added added

removed removed

Lines of Context:
1
 
/*
2
 
 * Copyright (c) 2004-2005 Sergey Lyubka <valenok@gmail.com>
3
 
 * All rights reserved
4
 
 *
5
 
 * "THE BEER-WARE LICENSE" (Revision 42):
6
 
 * Sergey Lyubka wrote this file.  As long as you retain this notice you
7
 
 * can do whatever you want with this stuff. If we meet some day, and you think
8
 
 * this stuff is worth it, you can buy me a beer in return.
9
 
 */
10
 
 
11
 
#include "defs.h"
12
 
 
13
 
#if !defined(NO_AUTH)
14
 
/*
15
 
 * Stringify binary data. Output buffer must be twice as big as input,
16
 
 * because each byte takes 2 bytes in string representation
17
 
 */
18
 
static void
19
 
bin2str(char *to, const unsigned char *p, size_t len)
20
 
{
21
 
        const char      *hex = "0123456789abcdef";
22
 
 
23
 
        for (;len--; p++) {
24
 
                *to++ = hex[p[0] >> 4];
25
 
                *to++ = hex[p[0] & 0x0f];
26
 
        }
27
 
}
28
 
 
29
 
/*
30
 
 * Return stringified MD5 hash for list of vectors.
31
 
 * buf must point to at least 32-bytes long buffer
32
 
 */
33
 
static void
34
 
md5(char *buf, ...)
35
 
{
36
 
        unsigned char   hash[16];
37
 
        const struct vec *v;
38
 
        va_list         ap;
39
 
        MD5_CTX ctx;
40
 
        int             i;
41
 
 
42
 
        MD5Init(&ctx);
43
 
 
44
 
        va_start(ap, buf);
45
 
        for (i = 0; (v = va_arg(ap, const struct vec *)) != NULL; i++) {
46
 
                assert(v->len >= 0);
47
 
                if (v->len == 0)
48
 
                        continue;
49
 
                if (i > 0)
50
 
                        MD5Update(&ctx, (unsigned char *) ":", 1);
51
 
                MD5Update(&ctx,(unsigned char *)v->ptr,(unsigned int)v->len);
52
 
        }
53
 
        va_end(ap);
54
 
 
55
 
        MD5Final(hash, &ctx);
56
 
        bin2str(buf, hash, sizeof(hash));
57
 
}
58
 
 
59
 
/*
60
 
 * Compare to vectors. Return 1 if they are equal
61
 
 */
62
 
static int
63
 
vcmp(const struct vec *v1, const struct vec *v2)
64
 
{
65
 
        return (v1->len == v2->len && !memcmp(v1->ptr, v2->ptr, v1->len));
66
 
}
67
 
 
68
 
struct digest {
69
 
        struct vec      user;
70
 
        struct vec      uri;
71
 
        struct vec      nonce;
72
 
        struct vec      cnonce;
73
 
        struct vec      resp;
74
 
        struct vec      qop;
75
 
        struct vec      nc;
76
 
};
77
 
 
78
 
static const struct auth_keyword {
79
 
        size_t          offset;
80
 
        struct vec      vec;
81
 
} known_auth_keywords[] = {
82
 
        {offsetof(struct digest, user),         {"username=",   9}},
83
 
        {offsetof(struct digest, cnonce),       {"cnonce=",     7}},
84
 
        {offsetof(struct digest, resp),         {"response=",   9}},
85
 
        {offsetof(struct digest, uri),          {"uri=",        4}},
86
 
        {offsetof(struct digest, qop),          {"qop=",        4}},
87
 
        {offsetof(struct digest, nc),           {"nc=",         3}},
88
 
        {offsetof(struct digest, nonce),        {"nonce=",      6}},
89
 
        {0,                                     {NULL,          0}}
90
 
};
91
 
 
92
 
static void
93
 
parse_authorization_header(const struct vec *h, struct digest *dig)
94
 
{
95
 
        const unsigned char     *p, *e, *s;
96
 
        struct vec              *v, vec;
97
 
        const struct auth_keyword *kw;
98
 
 
99
 
        (void) memset(dig, 0, sizeof(*dig));
100
 
        p = (unsigned char *) h->ptr + 7;
101
 
        e = (unsigned char *) h->ptr + h->len;
102
 
 
103
 
        while (p < e) {
104
 
 
105
 
                /* Skip spaces */
106
 
                while (p < e && (*p == ' ' || *p == ','))
107
 
                        p++;
108
 
 
109
 
                /* Skip to "=" */
110
 
                for (s = p; s < e && *s != '='; )
111
 
                        s++;
112
 
                s++;
113
 
 
114
 
                /* Is it known keyword ? */
115
 
                for (kw = known_auth_keywords; kw->vec.len > 0; kw++)
116
 
                        if (kw->vec.len <= s - p &&
117
 
                            !memcmp(p, kw->vec.ptr, kw->vec.len))
118
 
                                break;
119
 
 
120
 
                if (kw->vec.len == 0)
121
 
                        v = &vec;               /* Dummy placeholder    */
122
 
                else
123
 
                        v = (struct vec *) ((char *) dig + kw->offset);
124
 
 
125
 
                if (*s == '"') {
126
 
                        p = ++s;
127
 
                        while (p < e && *p != '"')
128
 
                                p++;
129
 
                } else {
130
 
                        p = s;
131
 
                        while (p < e && *p != ' ' && *p != ',')
132
 
                                p++;
133
 
                }
134
 
 
135
 
                v->ptr = (char *) s;
136
 
                v->len = p - s;
137
 
 
138
 
                if (*p == '"')
139
 
                        p++;
140
 
 
141
 
                DBG(("auth field [%.*s]", v->len, v->ptr));
142
 
        }
143
 
}
144
 
 
145
 
/*
146
 
 * Check the user's password, return 1 if OK
147
 
 */
148
 
static int
149
 
check_password(int method, const struct vec *ha1, const struct digest *digest)
150
 
{
151
 
        char            a2[32], resp[32];
152
 
        struct vec      vec_a2;
153
 
 
154
 
        /* XXX  Due to a bug in MSIE, we do not compare the URI  */
155
 
        /* Also, we do not check for authentication timeout */
156
 
        if (/*strcmp(dig->uri, c->ouri) != 0 || */
157
 
            digest->resp.len != 32 /*||
158
 
            now - strtoul(dig->nonce, NULL, 10) > 3600 */)
159
 
                return (0);
160
 
 
161
 
        md5(a2, &known_http_methods[method], &digest->uri, NULL);
162
 
        vec_a2.ptr = a2;
163
 
        vec_a2.len = sizeof(a2);
164
 
        md5(resp, ha1, &digest->nonce, &digest->nc,
165
 
            &digest->cnonce, &digest->qop, &vec_a2, NULL);
166
 
 
167
 
        return (!memcmp(resp, digest->resp.ptr, 32));
168
 
}
169
 
 
170
 
static FILE *
171
 
open_auth_file(struct shttpd_ctx *ctx, const char *path)
172
 
{
173
 
        char            name[FILENAME_MAX];
174
 
        const char      *p, *e;
175
 
        FILE            *fp = NULL;
176
 
        int             fd;
177
 
 
178
 
        if (ctx->options[OPT_AUTH_GPASSWD] != NULL) {
179
 
                /* Use global passwords file */
180
 
                my_snprintf(name, sizeof(name), "%s",
181
 
                    ctx->options[OPT_AUTH_GPASSWD]);
182
 
        } else {
183
 
                /* Try to find .htpasswd in requested directory */
184
 
                for (p = path, e = p + strlen(p) - 1; e > p; e--)
185
 
                        if (IS_DIRSEP_CHAR(*e))
186
 
                                break;
187
 
 
188
 
                assert(IS_DIRSEP_CHAR(*e));
189
 
                (void) my_snprintf(name, sizeof(name), "%.*s/%s",
190
 
                    (int) (e - p), p, HTPASSWD);
191
 
        }
192
 
 
193
 
        if ((fd = my_open(name, O_RDONLY, 0)) == -1) {
194
 
                DBG(("open_auth_file: open(%s)", name));
195
 
        } else if ((fp = fdopen(fd, "r")) == NULL) {
196
 
                DBG(("open_auth_file: fdopen(%s)", name));
197
 
                (void) close(fd);
198
 
        }
199
 
 
200
 
        return (fp);
201
 
}
202
 
 
203
 
/*
204
 
 * Parse the line from htpasswd file. Line should be in form of
205
 
 * "user:domain:ha1". Fill in the vector values. Return 1 if successful.
206
 
 */
207
 
static int
208
 
parse_htpasswd_line(const char *s, struct vec *user,
209
 
                                struct vec *domain, struct vec *ha1)
210
 
{
211
 
        user->len = domain->len = ha1->len = 0;
212
 
 
213
 
        for (user->ptr = s; *s != '\0' && *s != ':'; s++, user->len++);
214
 
        if (*s++ != ':')
215
 
                return (0);
216
 
 
217
 
        for (domain->ptr = s; *s != '\0' && *s != ':'; s++, domain->len++);
218
 
        if (*s++ != ':')
219
 
                return (0);
220
 
 
221
 
        for (ha1->ptr = s; *s != '\0' && !isspace(* (unsigned char *) s);
222
 
            s++, ha1->len++);
223
 
 
224
 
        DBG(("parse_htpasswd_line: [%.*s] [%.*s] [%.*s]", user->len, user->ptr,
225
 
            domain->len, domain->ptr, ha1->len, ha1->ptr));
226
 
 
227
 
        return (user->len > 0 && domain->len > 0 && ha1->len > 0);
228
 
}
229
 
 
230
 
/*
231
 
 * Authorize against the opened passwords file. Return 1 if authorized.
232
 
 */
233
 
static int
234
 
authorize(struct conn *c, FILE *fp)
235
 
{
236
 
        struct vec      *auth_vec = &c->ch.auth.v_vec;
237
 
        struct vec      *user_vec = &c->ch.user.v_vec;
238
 
        struct vec      user, domain, ha1;
239
 
        struct digest   digest;
240
 
        int             ok = 0;
241
 
        char            line[256];
242
 
 
243
 
        if (auth_vec->len > 20 &&
244
 
            !my_strncasecmp(auth_vec->ptr, "Digest ", 7)) {
245
 
 
246
 
                parse_authorization_header(auth_vec, &digest);
247
 
                *user_vec = digest.user;
248
 
 
249
 
                while (fgets(line, sizeof(line), fp) != NULL) {
250
 
 
251
 
                        if (!parse_htpasswd_line(line, &user, &domain, &ha1))
252
 
                                continue;
253
 
 
254
 
                        DBG(("[%.*s] [%.*s] [%.*s]", user.len, user.ptr,
255
 
                            domain.len, domain.ptr, ha1.len, ha1.ptr));
256
 
 
257
 
                        if (vcmp(user_vec, &user) &&
258
 
                            !memcmp(c->ctx->options[OPT_AUTH_REALM],
259
 
                            domain.ptr, domain.len)) {
260
 
                                ok = check_password(c->method, &ha1, &digest);
261
 
                                break;
262
 
                        }
263
 
                }
264
 
        }
265
 
 
266
 
        return (ok);
267
 
}
268
 
 
269
 
int
270
 
check_authorization(struct conn *c, const char *path)
271
 
{
272
 
        FILE            *fp = NULL;
273
 
        int             len, n, authorized = 1;
274
 
        const char      *p, *s = c->ctx->options[OPT_PROTECT];
275
 
        char            protected_path[FILENAME_MAX];
276
 
 
277
 
        FOR_EACH_WORD_IN_LIST(s, len) {
278
 
 
279
 
                if ((p = memchr(s, '=', len)) == NULL || p >= s + len || p == s)
280
 
                        continue;
281
 
 
282
 
                if (!memcmp(c->uri, s, p - s)) {
283
 
                        
284
 
                        n = s + len - p;
285
 
                        if (n > (int) sizeof(protected_path) - 1)
286
 
                                n = sizeof(protected_path) - 1;
287
 
 
288
 
                        my_strlcpy(protected_path, p + 1, n);
289
 
 
290
 
                        if ((fp = fopen(protected_path, "r")) == NULL)
291
 
                                elog(E_LOG, c, "check_auth: cannot open %s: %s",
292
 
                                    protected_path, strerror(errno));
293
 
                        break;
294
 
                }
295
 
        }
296
 
 
297
 
        if (fp == NULL)
298
 
                fp = open_auth_file(c->ctx, path);
299
 
 
300
 
        if (fp != NULL) {
301
 
                authorized = authorize(c, fp);
302
 
                (void) fclose(fp);
303
 
        }
304
 
 
305
 
        return (authorized);
306
 
}
307
 
 
308
 
int
309
 
is_authorized_for_put(struct conn *c)
310
 
{
311
 
        FILE    *fp;
312
 
        int     ret = 0;
313
 
 
314
 
        if ((fp = fopen(c->ctx->options[OPT_AUTH_PUT], "r")) != NULL) {
315
 
                ret = authorize(c, fp);
316
 
                (void) fclose(fp);
317
 
        }
318
 
 
319
 
        return (ret);
320
 
}
321
 
 
322
 
void
323
 
send_authorization_request(struct conn *c)
324
 
{
325
 
        char    buf[512];
326
 
 
327
 
        (void) my_snprintf(buf, sizeof(buf), "Unauthorized\r\n"
328
 
            "WWW-Authenticate: Digest qop=\"auth\", realm=\"%s\", "
329
 
            "nonce=\"%lu\"", c->ctx->options[OPT_AUTH_REALM],
330
 
            (unsigned long) current_time);
331
 
 
332
 
        send_server_error(c, 401, buf);
333
 
}
334
 
 
335
 
/*
336
 
 * Edit the passwords file.
337
 
 */
338
 
int
339
 
edit_passwords(const char *fname, const char *domain,
340
 
                const char *user, const char *pass)
341
 
{
342
 
        int             ret = EXIT_SUCCESS, found = 0;
343
 
        struct vec      u, d, p;
344
 
        char            line[512], tmp[FILENAME_MAX], ha1[32];
345
 
        FILE            *fp = NULL, *fp2 = NULL;
346
 
 
347
 
        (void) my_snprintf(tmp, sizeof(tmp), "%s.tmp", fname);
348
 
 
349
 
        /* Create the file if does not exist */
350
 
        if ((fp = fopen(fname, "a+")))
351
 
                (void) fclose(fp);
352
 
 
353
 
        /* Open the given file and temporary file */
354
 
        if ((fp = fopen(fname, "r")) == NULL)
355
 
                elog(E_FATAL, 0, "Cannot open %s: %s", fname, strerror(errno));
356
 
        else if ((fp2 = fopen(tmp, "w+")) == NULL)
357
 
                elog(E_FATAL, 0, "Cannot open %s: %s", tmp, strerror(errno));
358
 
 
359
 
        p.ptr = pass;
360
 
        p.len = strlen(pass);
361
 
 
362
 
        /* Copy the stuff to temporary file */
363
 
        while (fgets(line, sizeof(line), fp) != NULL) {
364
 
                u.ptr = line;
365
 
                if ((d.ptr = strchr(line, ':')) == NULL)
366
 
                        continue;
367
 
                u.len = d.ptr - u.ptr;
368
 
                d.ptr++;
369
 
                if (strchr(d.ptr, ':') == NULL)
370
 
                        continue;
371
 
                d.len = strchr(d.ptr, ':') - d.ptr;
372
 
 
373
 
                if ((int) strlen(user) == u.len &&
374
 
                    !memcmp(user, u.ptr, u.len) &&
375
 
                    (int) strlen(domain) == d.len &&
376
 
                    !memcmp(domain, d.ptr, d.len)) {
377
 
                        found++;
378
 
                        md5(ha1, &u, &d, &p, NULL);
379
 
                        (void) fprintf(fp2, "%s:%s:%.32s\n", user, domain, ha1);
380
 
                } else {
381
 
                        (void) fprintf(fp2, "%s", line);
382
 
                }
383
 
        }
384
 
 
385
 
        /* If new user, just add it */
386
 
        if (found == 0) {
387
 
                u.ptr = user; u.len = strlen(user);
388
 
                d.ptr = domain; d.len = strlen(domain);
389
 
                md5(ha1, &u, &d, &p, NULL);
390
 
                (void) fprintf(fp2, "%s:%s:%.32s\n", user, domain, ha1);
391
 
        }
392
 
 
393
 
        /* Close files */
394
 
        (void) fclose(fp);
395
 
        (void) fclose(fp2);
396
 
 
397
 
        /* Put the temp file in place of real file */
398
 
        (void) my_remove(fname);
399
 
        (void) my_rename(tmp, fname);
400
 
 
401
 
        return (ret);
402
 
}
403
 
#endif /* NO_AUTH */