summaryrefslogtreecommitdiff
path: root/src/http_auth.c
blob: 176a50f563d6a3877d259d2b8b110b1294175949 (plain)
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
166
167
168
169
170
171
172
173
174
175
176
177
178
179
180
181
182
183
184
185
186
187
188
189
190
191
192
193
194
195
196
197
198
199
200
201
202
203
204
205
206
207
208
209
210
211
212
213
214
215
216
217
218
/*
 * http_auth - HTTP Auth backend registration, low-level shared funcs
 *
 * Fully-rewritten from original
 * Copyright(c) 2016 Glenn Strauss gstrauss()gluelogic.com  All rights reserved
 * License: BSD 3-clause (same as lighttpd)
 */
#include "first.h"

#include "http_auth.h"
#include "http_header.h"

#include <stdlib.h>
#include <string.h>


static http_auth_scheme_t http_auth_schemes[8];

const http_auth_scheme_t * http_auth_scheme_get (const buffer *name)
{
    int i = 0;
    while (NULL != http_auth_schemes[i].name
           && 0 != strcmp(http_auth_schemes[i].name, name->ptr)) {
        ++i;
    }
    return (NULL != http_auth_schemes[i].name) ? http_auth_schemes+i : NULL;
}

void http_auth_scheme_set (const http_auth_scheme_t *scheme)
{
    unsigned int i = 0;
    while (NULL != http_auth_schemes[i].name) ++i;
    /*(must resize http_auth_schemes[] if too many different auth schemes)*/
    force_assert(i<(sizeof(http_auth_schemes)/sizeof(http_auth_scheme_t))-1);
    memcpy(http_auth_schemes+i, scheme, sizeof(http_auth_scheme_t));
}


static http_auth_backend_t http_auth_backends[12];

const http_auth_backend_t * http_auth_backend_get (const buffer *name)
{
    int i = 0;
    while (NULL != http_auth_backends[i].name
           && 0 != strcmp(http_auth_backends[i].name, name->ptr)) {
        ++i;
    }
    return (NULL != http_auth_backends[i].name) ? http_auth_backends+i : NULL;
}

void http_auth_backend_set (const http_auth_backend_t *backend)
{
    unsigned int i = 0;
    while (NULL != http_auth_backends[i].name) ++i;
    /*(must resize http_auth_backends[] if too many different auth backends)*/
    force_assert(i<(sizeof(http_auth_backends)/sizeof(http_auth_backend_t))-1);
    memcpy(http_auth_backends+i, backend, sizeof(http_auth_backend_t));
}


int http_auth_const_time_memeq (const void *a, const void *b, const size_t len)
{
    /* constant time memory compare, unless compiler figures it out
     * (similar to mod_secdownload.c:const_time_memeq()) */
    /* caller should prefer http_auth_const_time_memeq_pad()
     * if not operating on digests, which have defined lengths */
    /* Note: some libs provide similar funcs, e.g.
     * OpenSSL:
     *   int CRYPTO_memcmp(const void * in_a, const void * in_b, size_t len)
     * Note: some OS provide similar funcs, e.g.
     * OpenBSD: int timingsafe_bcmp(const void *b1, const void *b2, size_t len)
     * NetBSD: int consttime_memequal(void *b1, void *b2, size_t len)
     */
    const volatile unsigned char * const av = (const unsigned char *)a;
    const volatile unsigned char * const bv = (const unsigned char *)b;
    int diff = 0;
    for (size_t i = 0; i < len; ++i) {
        diff |= (av[i] ^ bv[i]);
    }
    return (0 == diff);
}


int http_auth_const_time_memeq_pad (const void *a, const size_t alen, const void *b, const size_t blen)
{
    /* constant time memory compare, unless compiler figures it out
     * (similar to mod_secdownload.c:const_time_memeq()) */
    /* round to next multiple of 64 to avoid potentially leaking exact
     * password length when subject to high precision timing attacks)
     * (not necessary when comparing digests, which have defined lengths)
     */
    /* Note: some libs provide similar funcs but might not obscure length, e.g.
     * OpenSSL:
     *   int CRYPTO_memcmp(const void * in_a, const void * in_b, size_t len)
     * Note: some OS provide similar funcs but might not obscure length, e.g.
     * OpenBSD: int timingsafe_bcmp(const void *b1, const void *b2, size_t len)
     * NetBSD: int consttime_memequal(void *b1, void *b2, size_t len)
     */
    const volatile unsigned char * const av = (const unsigned char *)a;
    const volatile unsigned char * const bv = (const unsigned char *)b;
    size_t lim = ((alen >= blen ? alen : blen) + 0x3F) & ~0x3F;
    int diff = (alen != blen); /*(never match if string length mismatch)*/
    for (size_t i = 0, j = 0; lim; --lim) {
        diff |= (av[i] ^ bv[j]);
        i += (i < alen);
        j += (j < blen);
    }
    return (0 == diff);
}


void http_auth_dumbdata_reset (void)
{
    memset(http_auth_schemes, 0, sizeof(http_auth_schemes));
    memset(http_auth_backends, 0, sizeof(http_auth_backends));
}


http_auth_require_t * http_auth_require_init (void)
{
    http_auth_require_t *require = calloc(1, sizeof(http_auth_require_t));
    force_assert(NULL != require);
    return require;
}

void http_auth_require_free (http_auth_require_t * const require)
{
    array_free_data(&require->user);
    array_free_data(&require->group);
    array_free_data(&require->host);
    free(require);
}

/* (case-sensitive version of array.c:array_get_index(),
 *  and common case expects small num of allowed tokens,
 *  so it is reasonably performant to simply walk the array) */
static int http_auth_array_contains (const array * const a, const char * const k, const size_t klen)
{
    for (size_t i = 0, used = a->used; i < used; ++i) {
        if (buffer_is_equal_string(&a->data[i]->key, k, klen)) {
            return 1;
        }
    }
    return 0;
}

int http_auth_match_rules (const http_auth_require_t * const require, const char * const user, const char * const group, const char * const host)
{
    if (NULL != user
        && (require->valid_user
            || http_auth_array_contains(&require->user, user, strlen(user)))) {
        return 1; /* match */
    }

    if (NULL != group
        && http_auth_array_contains(&require->group, group, strlen(group))) {
        return 1; /* match */
    }

    if (NULL != host
        && http_auth_array_contains(&require->host, host, strlen(host))) {
        return 1; /* match */
    }

    return 0; /* no match */
}

void http_auth_setenv(request_st * const r, const char *username, size_t ulen, const char *auth_type, size_t alen) {
    http_header_env_set(r, CONST_STR_LEN("REMOTE_USER"), username, ulen);
    http_header_env_set(r, CONST_STR_LEN("AUTH_TYPE"), auth_type, alen);
}

unsigned int http_auth_digest_len (int algo)
{
    if (algo & (HTTP_AUTH_DIGEST_SHA256 | HTTP_AUTH_DIGEST_SHA512_256)) {
        /* HTTP_AUTH_DIGEST_SHA512_256_BINLEN */
        return HTTP_AUTH_DIGEST_SHA256_BINLEN;
    }
    if (algo & HTTP_AUTH_DIGEST_MD5) {
        return HTTP_AUTH_DIGEST_MD5_BINLEN;
    }

    return 0;
}

int http_auth_digest_hex2bin (const char *hexstr, size_t len, unsigned char *bin, size_t binlen)
{
    /* validate and transform 32-byte MD5 hex string to 16-byte binary MD5,
     * or 64-byte SHA-256 or SHA-512-256 hex string to 32-byte binary digest */
    if (len > (binlen << 1)) return -1;
    for (int i = 0, ilen = (int)len; i < ilen; i+=2) {
        int hi = hexstr[i];
        int lo = hexstr[i+1];
        if ('0' <= hi && hi <= '9')                    hi -= '0';
        else if ((hi |= 0x20), 'a' <= hi && hi <= 'f') hi += -'a' + 10;
        else                                           return -1;
        if ('0' <= lo && lo <= '9')                    lo -= '0';
        else if ((lo |= 0x20), 'a' <= lo && lo <= 'f') lo += -'a' + 10;
        else                                           return -1;
        bin[(i >> 1)] = (unsigned char)((hi << 4) | lo);
    }
    return 0;
}

#if 0
int http_auth_md5_hex2lc (char *md5hex)
{
    /* validate and transform 32-byte MD5 hex string to lowercase */
    int i;
    for (i = 0; md5hex[i]; ++i) {
        int c = md5hex[i];
        if ('0' <= c && c <= '9')                   continue;
        else if ((c |= 0x20), 'a' <= c && c <= 'f') md5hex[i] = c;
        else                                        return -1;
    }
    return (32 == i) ? 0 : -1; /*(Note: char *md5hex must be a 32-char string)*/
}
#endif