blob: 1357cad1a30aef921940bc6fb7c0c0addf16fce7 [file] [log] [blame]
James Kuszmaul82f6c042021-01-17 11:30:16 -08001/**
2 * @file sip/auth.c SIP Authentication
3 *
4 * Copyright (C) 2010 Creytiv.com
5 */
6#include <string.h>
7#include <re_types.h>
8#include <re_mem.h>
9#include <re_fmt.h>
10#include <re_mbuf.h>
11#include <re_uri.h>
12#include <re_list.h>
13#include <re_sa.h>
14#include <re_sys.h>
15#include <re_md5.h>
16#include <re_httpauth.h>
17#include <re_udp.h>
18#include <re_msg.h>
19#include <re_sip.h>
20#include "sip.h"
21
22
23struct sip_auth {
24 struct list realml;
25 sip_auth_h *authh;
26 void *arg;
27 bool ref;
28 int err;
29};
30
31
32struct realm {
33 struct le le;
34 char *realm;
35 char *nonce;
36 char *qop;
37 char *opaque;
38 char *user;
39 char *pass;
40 uint32_t nc;
41 enum sip_hdrid hdr;
42};
43
44
45static int dummy_handler(char **user, char **pass, const char *rlm, void *arg)
46{
47 (void)user;
48 (void)pass;
49 (void)rlm;
50 (void)arg;
51
52 return EAUTH;
53}
54
55
56static void realm_destructor(void *arg)
57{
58 struct realm *realm = arg;
59
60 list_unlink(&realm->le);
61 mem_deref(realm->realm);
62 mem_deref(realm->nonce);
63 mem_deref(realm->qop);
64 mem_deref(realm->opaque);
65 mem_deref(realm->user);
66 mem_deref(realm->pass);
67}
68
69
70static void auth_destructor(void *arg)
71{
72 struct sip_auth *auth = arg;
73
74 if (auth->ref)
75 mem_deref(auth->arg);
76
77 list_flush(&auth->realml);
78}
79
80
81static int mkdigest(uint8_t *digest, const struct realm *realm,
82 const char *met, const char *uri, uint64_t cnonce)
83{
84 uint8_t ha1[MD5_SIZE], ha2[MD5_SIZE];
85 int err;
86
87 err = md5_printf(ha1, "%s:%s:%s",
88 realm->user, realm->realm, realm->pass);
89 if (err)
90 return err;
91
92 err = md5_printf(ha2, "%s:%s", met, uri);
93 if (err)
94 return err;
95
96 if (realm->qop)
97 return md5_printf(digest, "%w:%s:%08x:%016llx:auth:%w",
98 ha1, sizeof(ha1),
99 realm->nonce,
100 realm->nc,
101 cnonce,
102 ha2, sizeof(ha2));
103 else
104 return md5_printf(digest, "%w:%s:%w",
105 ha1, sizeof(ha1),
106 realm->nonce,
107 ha2, sizeof(ha2));
108}
109
110
111static bool cmp_handler(struct le *le, void *arg)
112{
113 struct realm *realm = le->data;
114 struct pl *chrealm = arg;
115
116 /* handle multiple authenticate headers with equal realm value */
117 if (realm->nc == 1)
118 return false;
119
120 return 0 == pl_strcasecmp(chrealm, realm->realm);
121}
122
123
124static bool auth_handler(const struct sip_hdr *hdr, const struct sip_msg *msg,
125 void *arg)
126{
127 struct httpauth_digest_chall ch;
128 struct sip_auth *auth = arg;
129 struct realm *realm = NULL;
130 int err;
131 (void)msg;
132
133 if (httpauth_digest_challenge_decode(&ch, &hdr->val)) {
134 err = EBADMSG;
135 goto out;
136 }
137
138 if (pl_isset(&ch.algorithm) && pl_strcasecmp(&ch.algorithm, "md5")) {
139 err = ENOSYS;
140 goto out;
141 }
142
143 realm = list_ledata(list_apply(&auth->realml, true, cmp_handler,
144 &ch.realm));
145 if (!realm) {
146 realm = mem_zalloc(sizeof(*realm), realm_destructor);
147 if (!realm) {
148 err = ENOMEM;
149 goto out;
150 }
151
152 list_append(&auth->realml, &realm->le, realm);
153
154 err = pl_strdup(&realm->realm, &ch.realm);
155 if (err)
156 goto out;
157
158 err = auth->authh(&realm->user, &realm->pass,
159 realm->realm, auth->arg);
160 if (err)
161 goto out;
162 }
163 else {
164 if (!pl_isset(&ch.stale) || pl_strcasecmp(&ch.stale, "true")) {
165 err = EAUTH;
166 goto out;
167 }
168
169 realm->nonce = mem_deref(realm->nonce);
170 realm->qop = mem_deref(realm->qop);
171 realm->opaque = mem_deref(realm->opaque);
172 }
173
174 realm->hdr = hdr->id;
175 realm->nc = 1;
176
177 err = pl_strdup(&realm->nonce, &ch.nonce);
178
179 if (pl_isset(&ch.qop))
180 err |= pl_strdup(&realm->qop, &ch.qop);
181
182 if (pl_isset(&ch.opaque))
183 err |= pl_strdup(&realm->opaque, &ch.opaque);
184
185 out:
186 if (err) {
187 mem_deref(realm);
188 auth->err = err;
189 return true;
190 }
191
192 return false;
193}
194
195
196/**
197 * Update a SIP authentication state from a SIP message
198 *
199 * @param auth SIP Authentication state
200 * @param msg SIP Message
201 *
202 * @return 0 if success, otherwise errorcode
203 */
204int sip_auth_authenticate(struct sip_auth *auth, const struct sip_msg *msg)
205{
206 if (!auth || !msg)
207 return EINVAL;
208
209 if (sip_msg_hdr_apply(msg, true, SIP_HDR_WWW_AUTHENTICATE,
210 auth_handler, auth))
211 return auth->err;
212
213 if (sip_msg_hdr_apply(msg, true, SIP_HDR_PROXY_AUTHENTICATE,
214 auth_handler, auth))
215 return auth->err;
216
217 return 0;
218}
219
220
221int sip_auth_encode(struct mbuf *mb, struct sip_auth *auth, const char *met,
222 const char *uri)
223{
224 struct le *le;
225 int err = 0;
226
227 if (!mb || !auth || !met || !uri)
228 return EINVAL;
229
230 for (le = auth->realml.head; le; le = le->next) {
231
232 const uint64_t cnonce = rand_u64();
233 struct realm *realm = le->data;
234 uint8_t digest[MD5_SIZE];
235
236 err = mkdigest(digest, realm, met, uri, cnonce);
237 if (err)
238 break;
239
240 switch (realm->hdr) {
241
242 case SIP_HDR_WWW_AUTHENTICATE:
243 err = mbuf_write_str(mb, "Authorization: ");
244 break;
245
246 case SIP_HDR_PROXY_AUTHENTICATE:
247 err = mbuf_write_str(mb, "Proxy-Authorization: ");
248 break;
249
250 default:
251 continue;
252 }
253
254 err |= mbuf_printf(mb, "Digest username=\"%s\"", realm->user);
255 err |= mbuf_printf(mb, ", realm=\"%s\"", realm->realm);
256 err |= mbuf_printf(mb, ", nonce=\"%s\"", realm->nonce);
257 err |= mbuf_printf(mb, ", uri=\"%s\"", uri);
258 err |= mbuf_printf(mb, ", response=\"%w\"",
259 digest, sizeof(digest));
260
261 if (realm->opaque)
262 err |= mbuf_printf(mb, ", opaque=\"%s\"",
263 realm->opaque);
264
265 if (realm->qop) {
266 err |= mbuf_printf(mb, ", cnonce=\"%016llx\"", cnonce);
267 err |= mbuf_write_str(mb, ", qop=auth");
268 err |= mbuf_printf(mb, ", nc=%08x", realm->nc);
269 }
270
271 ++realm->nc;
272
273 err |= mbuf_write_str(mb, "\r\n");
274 if (err)
275 break;
276 }
277
278 return err;
279}
280
281
282/**
283 * Allocate a SIP authentication state
284 *
285 * @param authp Pointer to allocated SIP authentication state
286 * @param authh Authentication handler
287 * @param arg Handler argument
288 * @param ref True to mem_ref() argument
289 *
290 * @return 0 if success, otherwise errorcode
291 */
292int sip_auth_alloc(struct sip_auth **authp, sip_auth_h *authh,
293 void *arg, bool ref)
294{
295 struct sip_auth *auth;
296
297 if (!authp)
298 return EINVAL;
299
300 auth = mem_zalloc(sizeof(*auth), auth_destructor);
301 if (!auth)
302 return ENOMEM;
303
304 auth->authh = authh ? authh : dummy_handler;
305 auth->arg = ref ? mem_ref(arg) : arg;
306 auth->ref = ref;
307
308 *authp = auth;
309
310 return 0;
311}
312
313
314/**
315 * Reset a SIP authentication state
316 *
317 * @param auth SIP Authentication state
318 */
319void sip_auth_reset(struct sip_auth *auth)
320{
321 if (!auth)
322 return;
323
324 list_flush(&auth->realml);
325}