source: rtems/cpukit/shttpd/auth.c @ 039267fc

4.104.115
Last change on this file since 039267fc was 484cd8d, checked in by Ralf Corsepius <ralf.corsepius@…>, on 06/11/07 at 13:24:29

Import from shttpd-1.37.

  • Property mode set to 100644
File size: 8.9 KB
Line 
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 */
18static void
19bin2str(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 */
33static void
34md5(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 */
62static int
63vcmp(const struct vec *v1, const struct vec *v2)
64{
65        return (v1->len == v2->len && !memcmp(v1->ptr, v2->ptr, v1->len));
66}
67
68struct 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
78static 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
92static void
93parse_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 */
148static int
149check_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
170static FILE *
171open_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->global_passwd_file) {
179                /* Use global passwords file */
180                my_snprintf(name, sizeof(name), "%s", ctx->global_passwd_file);
181        } else {
182                /* Try to find .htpasswd in requested directory */
183                for (p = path, e = p + strlen(p) - 1; e > p; e--)
184                        if (IS_DIRSEP_CHAR(*e))
185                                break;
186
187                assert(IS_DIRSEP_CHAR(*e));
188                (void) my_snprintf(name, sizeof(name), "%.*s/%s",
189                    (int) (e - p), p, HTPASSWD);
190        }
191
192        if ((fd = my_open(name, O_RDONLY, 0)) == -1) {
193                DBG(("open_auth_file: open(%s)", name));
194        } else if ((fp = fdopen(fd, "r")) == NULL) {
195                DBG(("open_auth_file: fdopen(%s)", name));
196                (void) close(fd);
197        }
198
199        return (fp);
200}
201
202/*
203 * Parse the line from htpasswd file. Line should be in form of
204 * "user:domain:ha1". Fill in the vector values. Return 1 if successful.
205 */
206static int
207parse_htpasswd_line(const char *s, struct vec *user,
208                                struct vec *domain, struct vec *ha1)
209{
210        user->len = domain->len = ha1->len = 0;
211
212        for (user->ptr = s; *s != '\0' && *s != ':'; s++, user->len++);
213        if (*s++ != ':')
214                return (0);
215
216        for (domain->ptr = s; *s != '\0' && *s != ':'; s++, domain->len++);
217        if (*s++ != ':')
218                return (0);
219
220        for (ha1->ptr = s; *s != '\0' && !isspace(* (unsigned char *) s);
221            s++, ha1->len++);
222
223        DBG(("parse_htpasswd_line: [%.*s] [%.*s] [%.*s]", user->len, user->ptr,
224            domain->len, domain->ptr, ha1->len, ha1->ptr));
225
226        return (user->len > 0 && domain->len > 0 && ha1->len > 0);
227}
228
229/*
230 * Authorize against the opened passwords file. Return 1 if authorized.
231 */
232static int
233authorize(struct conn *c, FILE *fp)
234{
235        struct vec      *auth_vec = &c->ch.auth.v_vec;
236        struct vec      *user_vec = &c->ch.user.v_vec;
237        struct vec      user, domain, ha1;
238        struct digest   digest;
239        int             ok = 0;
240        char            line[256];
241
242        if (auth_vec->len > 20 &&
243            !my_strncasecmp(auth_vec->ptr, "Digest ", 7)) {
244
245                parse_authorization_header(auth_vec, &digest);
246                *user_vec = digest.user;
247
248                while (fgets(line, sizeof(line), fp) != NULL) {
249
250                        if (!parse_htpasswd_line(line, &user, &domain, &ha1))
251                                continue;
252
253                        DBG(("[%.*s] [%.*s] [%.*s]", user.len, user.ptr,
254                            domain.len, domain.ptr, ha1.len, ha1.ptr));
255
256                        if (vcmp(user_vec, &user) && !memcmp(c->ctx->auth_realm,
257                            domain.ptr, domain.len)) {
258                                ok = check_password(c->method, &ha1, &digest);
259                                break;
260                        }
261                }
262        }
263
264        return (ok);
265}
266
267int
268check_authorization(struct conn *c, const char *path)
269{
270        FILE                    *fp = NULL;
271        int                     authorized = 1;
272       
273#ifdef EMBEDDED
274        struct llhead   *lp;
275        struct uri_auth *auth;
276
277        /* Check, is this URL protected by shttpd_protect_url() */
278        LL_FOREACH(&c->ctx->uri_auths, lp) {
279                auth = LL_ENTRY(lp, struct uri_auth, link);
280                if (!strncmp(c->uri, auth->uri, auth->uri_len)) {
281                        fp = fopen(auth->file_name, "r");
282                        break;
283                }
284        }
285#endif /* EMBEDDED */
286       
287        if (fp == NULL)
288                fp = open_auth_file(c->ctx, path);
289
290        if (fp != NULL) {
291                authorized = authorize(c, fp);
292                (void) fclose(fp);
293        }
294
295        return (authorized);
296}
297
298int
299is_authorized_for_put(struct conn *c)
300{
301        FILE    *fp;
302        int     ret = 0;
303
304        if ((fp = fopen(c->ctx->put_auth_file, "r")) != NULL) {
305                ret = authorize(c, fp);
306                (void) fclose(fp);
307        }
308
309        return (ret);
310}
311
312void
313send_authorization_request(struct conn *c)
314{
315        char    buf[512];
316
317        (void) my_snprintf(buf, sizeof(buf), "Unauthorized\r\n"
318            "WWW-Authenticate: Digest qop=\"auth\", realm=\"%s\", "
319            "nonce=\"%lu\"", c->ctx->auth_realm, (unsigned long) current_time);
320
321        send_server_error(c, 401, buf);
322}
323
324/*
325 * Edit the passwords file.
326 */
327int
328edit_passwords(const char *fname, const char *domain,
329                const char *user, const char *pass)
330{
331        int             ret = EXIT_SUCCESS, found = 0;
332        struct vec      u, d, p;
333        char            line[512], tmp[FILENAME_MAX], ha1[32];
334        FILE            *fp = NULL, *fp2 = NULL;
335
336        (void) my_snprintf(tmp, sizeof(tmp), "%s.tmp", fname);
337
338        /* Create the file if does not exist */
339        if ((fp = fopen(fname, "a+")))
340                (void) fclose(fp);
341
342        /* Open the given file and temporary file */
343        if ((fp = fopen(fname, "r")) == NULL)
344                elog(E_FATAL, 0, "Cannot open %s: %s", fname, strerror(errno));
345        else if ((fp2 = fopen(tmp, "w+")) == NULL)
346                elog(E_FATAL, 0, "Cannot open %s: %s", tmp, strerror(errno));
347
348        p.ptr = pass;
349        p.len = strlen(pass);
350
351        /* Copy the stuff to temporary file */
352        while (fgets(line, sizeof(line), fp) != NULL) {
353                u.ptr = line;
354                if ((d.ptr = strchr(line, ':')) == NULL)
355                        continue;
356                u.len = d.ptr - u.ptr;
357                d.ptr++;
358                if (strchr(d.ptr, ':') == NULL)
359                        continue;
360                d.len = strchr(d.ptr, ':') - d.ptr;
361
362                if ((int) strlen(user) == u.len &&
363                    !memcmp(user, u.ptr, u.len) &&
364                    (int) strlen(domain) == d.len &&
365                    !memcmp(domain, d.ptr, d.len)) {
366                        found++;
367                        md5(ha1, &u, &d, &p, NULL);
368                        (void) fprintf(fp2, "%s:%s:%.32s\n", user, domain, ha1);
369                } else {
370                        (void) fprintf(fp2, "%s", line);
371                }
372        }
373
374        /* If new user, just add it */
375        if (found == 0) {
376                u.ptr = user; u.len = strlen(user);
377                d.ptr = domain; d.len = strlen(domain);
378                md5(ha1, &u, &d, &p, NULL);
379                (void) fprintf(fp2, "%s:%s:%.32s\n", user, domain, ha1);
380        }
381
382        /* Close files */
383        (void) fclose(fp);
384        (void) fclose(fp2);
385
386        /* Put the temp file in place of real file */
387        (void) my_remove(fname);
388        (void) my_rename(tmp, fname);
389
390        return (ret);
391}
392#endif /* NO_AUTH */
Note: See TracBrowser for help on using the repository browser.