1 /*
2 * This file is part of the Sofia-SIP package
3 *
4 * Copyright (C) 2005 Nokia Corporation.
5 *
6 * Contact: Pekka Pessi <pekka.pessi@nokia.com>
7 *
8 * This library is free software; you can redistribute it and/or
9 * modify it under the terms of the GNU Lesser General Public License
10 * as published by the Free Software Foundation; either version 2.1 of
11 * the License, or (at your option) any later version.
12 *
13 * This library is distributed in the hope that it will be useful, but
14 * WITHOUT ANY WARRANTY; without even the implied warranty of
15 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
16 * Lesser General Public License for more details.
17 *
18 * You should have received a copy of the GNU Lesser General Public
19 * License along with this library; if not, write to the Free Software
20 * Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA
21 * 02110-1301 USA
22 *
23 */
24
25 /**@CFILE auth_client.c Authenticators for SIP client
26 *
27 * @author Pekka Pessi <Pekka.Pessi@nokia.com>
28 *
29 * @date Created: Wed Feb 14 18:32:58 2001 ppessi
30 */
31
32 #include "config.h"
33
34 #define SOFIA_EXTEND_AUTH_CLIENT 1
35
36 #include <sofia-sip/su.h>
37 #include <sofia-sip/su_md5.h>
38
39 #include "sofia-sip/auth_common.h"
40 #include "sofia-sip/auth_client.h"
41 #include "sofia-sip/auth_client_plugin.h"
42
43 #include <sofia-sip/msg_types.h>
44 #include <sofia-sip/msg_header.h>
45
46 #include <sofia-sip/auth_digest.h>
47
48 #include <sofia-sip/base64.h>
49 #include <sofia-sip/bnf.h>
50 #include <sofia-sip/su_uniqueid.h>
51 #include <sofia-sip/su_string.h>
52
53 #include <sofia-sip/su_debug.h>
54
55 #include <stddef.h>
56 #include <stdlib.h>
57 #include <string.h>
58
59 #include <assert.h>
60
61 static auth_client_t *ca_create(su_home_t *home,
62 char const *scheme,
63 char const *realm);
64
65 static void ca_destroy(su_home_t *home, auth_client_t *ca);
66
67 static int ca_challenge(auth_client_t *ca,
68 msg_auth_t const *auth,
69 msg_hclass_t *credential_class,
70 char const *scheme,
71 char const *realm);
72
73 static int ca_info(auth_client_t *ca,
74 msg_auth_info_t const *ai,
75 msg_hclass_t *credential_class);
76
77 static int ca_credentials(auth_client_t *ca,
78 char const *scheme,
79 char const *realm,
80 char const *user,
81 char const *pass);
82
83 static int ca_clear_credentials(auth_client_t *ca);
84
85 static int ca_has_authorization(auth_client_t const *ca);
86
87
88 /** Initialize authenticators.
89 *
90 * The function auc_challenge() merges the challenge @a ch to the list of
91 * authenticators @a auc_list.
92 *
93 * @param[in,out] auc_list list of authenticators to be updated
94 * @param[in,out] home memory home used for allocating authenticators
95 * @param[in] ch challenge to be processed
96 * @param[in] crcl credential class
97 *
98 * @retval 1 when at least one challenge was updated
99 * @retval 0 when there was no new challenges
100 * @retval -1 upon an error
101 */
auc_challenge(auth_client_t ** auc_list,su_home_t * home,msg_auth_t const * ch,msg_hclass_t * crcl)102 int auc_challenge(auth_client_t **auc_list,
103 su_home_t *home,
104 msg_auth_t const *ch,
105 msg_hclass_t *crcl)
106 {
107 auth_client_t **cca;
108 int retval = 0;
109
110 /* Go through each challenge in Authenticate or Proxy-Authenticate headers */
111 for (; ch; ch = ch->au_next) {
112 char const *scheme = ch->au_scheme;
113 char const *realm = msg_header_find_param(ch->au_common, "realm=");
114 int matched = 0, updated;
115
116 if (!scheme || !realm)
117 continue;
118
119 /* Update matching authenticator */
120 for (cca = auc_list; (*cca); cca = &(*cca)->ca_next) {
121 updated = ca_challenge((*cca), ch, crcl, scheme, realm);
122 if (updated < 0)
123 return -1;
124 if (updated == 0)
125 continue; /* No match, next */
126 matched = 1;
127 if (updated > 1)
128 retval = 1; /* Updated authenticator */
129 }
130
131 if (!matched) {
132 /* There was no matching authenticator, create a new one */
133 *cca = ca_create(home, scheme, realm);
134
135 if (*cca == NULL) {
136 return -1;
137 }
138 else if (ca_challenge((*cca), ch, crcl, scheme, realm) < 0) {
139 ca_destroy(home, *cca), *cca = NULL;
140 return -1;
141 }
142 /* XXX - case w/ unknown authentication scheme */
143 else
144 retval = 1; /* Updated authenticator */
145 }
146 }
147
148 return retval;
149 }
150
151 /** Update authentication client.
152 *
153 * @retval -1 upon an error
154 * @retval 0 when challenge did not match
155 * @retval 1 when challenge did match but was not updated
156 * @retval 2 when challenge did match and updated client
157 */
158 static
ca_challenge(auth_client_t * ca,msg_auth_t const * ch,msg_hclass_t * credential_class,char const * scheme,char const * realm)159 int ca_challenge(auth_client_t *ca,
160 msg_auth_t const *ch,
161 msg_hclass_t *credential_class,
162 char const *scheme,
163 char const *realm)
164 {
165 int stale = 0;
166
167 assert(ca); assert(ch);
168
169 if (!ca || !ch)
170 return -1;
171
172 if (!su_casematch(ca->ca_scheme, scheme))
173 return 0;
174 if (!su_strmatch(ca->ca_realm, realm))
175 return 0;
176
177 if (ca->ca_credential_class &&
178 ca->ca_credential_class != credential_class)
179 return 0;
180
181 if (!ca->ca_auc) {
182 ca->ca_credential_class = credential_class;
183 return 1;
184 }
185
186 if (ca->ca_auc->auc_challenge)
187 stale = ca->ca_auc->auc_challenge(ca, ch);
188
189 if (AUTH_CLIENT_IS_EXTENDED(ca))
190 ca->ca_clear = 0;
191
192 if (stale < 0)
193 return -1;
194
195 if (!ca->ca_credential_class)
196 stale = 2, ca->ca_credential_class = credential_class;
197
198 return stale > 1 ? 2 : 1;
199 }
200
201 /** Store authentication info to authenticators.
202 *
203 * The function auc_info() feeds the authentication data from the @b
204 * Authentication-Info header @a info to the list of authenticators @a
205 * auc_list.
206 *
207 * @param[in,out] auc_list list of authenticators to be updated
208 * @param[in] info info header to be processed
209 * @param[in] credential_class corresponding credential class
210 *
211 * The authentication info can be in either @AuthenticationInfo or in
212 * @ProxyAuthenticationInfo headers. If the header is @AuthenticationInfo,
213 * the @a credential_class should be #sip_authorization_class or
214 * #http_authorization_class. Likewise, If the header is
215 * @ProxyAuthenticationInfo, the @a credential_class should be
216 * #sip_proxy_authorization_class or #http_proxy_authorization_class.
217
218 * The authentication into header usually contains next nonce or mutual
219 * authentication information. Currently, only the nextnonce parameter is
220 * processed.
221 *
222 * @bug
223 * In principle, SIP allows more than one challenge for a single request.
224 * For example, there can be multiple proxies that each challenge the
225 * client. The result of storing authentication info can be quite unexpected
226 * if there are more than one authenticator with the given type (specified
227 * by @a credential_class).
228 *
229 * @retval number of challenges to updated
230 * @retval 0 when there was no challenge to update
231 * @retval -1 upon an error
232 *
233 * @NEW_1_12_5.
234 */
auc_info(auth_client_t ** auc_list,msg_auth_info_t const * info,msg_hclass_t * credential_class)235 int auc_info(auth_client_t **auc_list,
236 msg_auth_info_t const *info,
237 msg_hclass_t *credential_class)
238 {
239 auth_client_t *ca;
240 int retval = 0;
241
242 /* Go through each challenge in Authenticate or Proxy-Authenticate headers */
243
244 /* Update matching authenticator */
245 for (ca = *auc_list; ca; ca = ca->ca_next) {
246 int updated = ca_info(ca, info, credential_class);
247 if (updated < 0)
248 return -1;
249 if (updated >= 1)
250 retval = 1; /* Updated authenticator */
251 }
252
253 return retval;
254 }
255
256 /** Update authentication client with authentication info.
257 *
258 * @retval -1 upon an error
259 * @retval 0 when challenge did not match
260 * @retval 1 when challenge did match but was not updated
261 * @retval 2 when challenge did match and updated client
262 */
263 static
ca_info(auth_client_t * ca,msg_auth_info_t const * info,msg_hclass_t * credential_class)264 int ca_info(auth_client_t *ca,
265 msg_auth_info_t const *info,
266 msg_hclass_t *credential_class)
267 {
268 assert(ca); assert(info);
269
270 if (!ca || !info)
271 return -1;
272
273 if (!ca->ca_credential_class)
274 return 0;
275
276 if (ca->ca_credential_class != credential_class)
277 return 0;
278
279 if (!ca->ca_auc
280 || (size_t)ca->ca_auc->auc_plugin_size <=
281 offsetof(auth_client_plugin_t, auc_info)
282 || !ca->ca_auc->auc_info)
283 return 0;
284
285 return ca->ca_auc->auc_info(ca, info);
286 }
287
288
289 /**Feed authentication data to the authenticator.
290 *
291 * The function auc_credentials() is used to provide the authenticators in
292 * with authentication data (user name, secret). The authentication data
293 * has format as follows:
294 *
295 * scheme:"realm":user:pass
296 *
297 * For instance, @c Basic:"nokia-proxy":ppessi:verysecret
298 *
299 * @todo The authentication data format sucks.
300 *
301 * @param[in,out] auc_list list of authenticators
302 * @param[in,out] home memory home used for allocations
303 * @param[in] data colon-separated authentication data
304 *
305 * @retval >0 when successful
306 * @retval 0 if not authenticator matched with @a data
307 * @retval -1 upon an error
308 */
auc_credentials(auth_client_t ** auc_list,su_home_t * home,char const * data)309 int auc_credentials(auth_client_t **auc_list, su_home_t *home,
310 char const *data)
311 {
312 int retval = 0, match;
313 char *s0, *s;
314 char *scheme = NULL, *user = NULL, *pass = NULL, *realm = NULL;
315
316 s0 = s = su_strdup(NULL, data);
317
318 /* Parse authentication data */
319 /* Data is string like "Basic:\"agni\":user1:secret"
320 or "Basic:\"[fe80::204:23ff:fea7:d60a]\":user1:secret" (IPv6)
321 or "Basic:\"Use \\\"interesting\\\" username and password here:\":user1:secret"
322 */
323 if (s && (s = strchr(scheme = s, ':')))
324 *s++ = 0;
325 if (s) {
326 if (*s == '"') {
327 realm = s;
328 s += span_quoted(s);
329 if (*s == ':')
330 *s++ = 0;
331 else
332 realm = NULL, s = NULL;
333 }
334 else
335 s = NULL;
336 }
337 if (s && (s = strchr(user = s, ':')))
338 *s++ = 0;
339 if (s && (s = strchr(pass = s, ':')))
340 *s++ = 0;
341
342 if (scheme && realm && user && pass) {
343 for (; *auc_list; auc_list = &(*auc_list)->ca_next) {
344 match = ca_credentials(*auc_list, scheme, realm, user, pass);
345 if (match < 0) {
346 retval = -1;
347 break;
348 }
349 if (match)
350 retval++;
351 }
352 }
353
354 su_free(NULL, s0);
355
356 return retval;
357 }
358
359 /**Feed authentication data to the authenticator.
360 *
361 * The function auc_credentials() is used to provide the authenticators in
362 * with authentication tuple (scheme, realm, user name, secret).
363 *
364 * scheme:"realm":user:pass
365 *
366 * @param[in,out] auc_list list of authenticators
367 * @param[in] scheme scheme to use (NULL, if any)
368 * @param[in] realm realm to use (NULL, if any)
369 * @param[in] user username
370 * @param[in] pass password
371 *
372 * @retval >0 or number of updated clients when successful
373 * @retval 0 when no client was updated
374 * @retval -1 upon an error
375 */
auc_all_credentials(auth_client_t ** auc_list,char const * scheme,char const * realm,char const * user,char const * pass)376 int auc_all_credentials(auth_client_t **auc_list,
377 char const *scheme,
378 char const *realm,
379 char const *user,
380 char const *pass)
381 {
382 int retval = 0, match;
383
384 #if HAVE_SC_CRED_H
385 /* XXX: add */
386 #endif
387
388 if (user && pass) {
389 for (; *auc_list; auc_list = &(*auc_list)->ca_next) {
390 match = ca_credentials(*auc_list, scheme, realm, user, pass);
391 if (match < 0)
392 return -1;
393 if (match)
394 retval++;
395 }
396 }
397
398 return retval;
399 }
400
ca_credentials(auth_client_t * ca,char const * scheme,char const * realm,char const * user,char const * pass)401 int ca_credentials(auth_client_t *ca,
402 char const *scheme,
403 char const *realm,
404 char const *user,
405 char const *pass)
406 {
407 char *new_user, *new_pass;
408 char *old_user, *old_pass;
409
410 assert(ca);
411
412 if (!ca || !ca->ca_scheme || !ca->ca_realm)
413 return -1;
414
415 if ((scheme != NULL && !su_casematch(scheme, ca->ca_scheme)) ||
416 (realm != NULL && !su_strmatch(realm, ca->ca_realm)))
417 return 0;
418
419 old_user = ca->ca_user, old_pass = ca->ca_pass;
420
421 if (su_strmatch(user, old_user) && su_strmatch(pass, old_pass))
422 return 0;
423
424 new_user = su_strdup(ca->ca_home, user);
425 new_pass = su_strdup(ca->ca_home, pass);
426
427 if (!new_user || !new_pass)
428 return -1;
429
430 ca->ca_user = new_user, ca->ca_pass = new_pass;
431 if (AUTH_CLIENT_IS_EXTENDED(ca))
432 ca->ca_clear = 0;
433
434 su_free(ca->ca_home, old_user);
435 su_free(ca->ca_home, old_pass);
436
437 return 1;
438 }
439
440
441 /** Copy authentication data from @a src to @a dst.
442 *
443 * @retval >0 if credentials were copied
444 * @retval 0 if there was no credentials to copy
445 * @retval <0 if an error occurred.
446 */
auc_copy_credentials(auth_client_t ** dst,auth_client_t const * src)447 int auc_copy_credentials(auth_client_t **dst,
448 auth_client_t const *src)
449 {
450 int retval = 0;
451
452 if (!dst)
453 return -1;
454
455 for (;*dst; dst = &(*dst)->ca_next) {
456 auth_client_t *d = *dst;
457 auth_client_t const *ca;
458
459 for (ca = src; ca; ca = ca->ca_next) {
460 char *u, *p;
461 if (!ca->ca_user || !ca->ca_pass)
462 continue;
463 if (AUTH_CLIENT_IS_EXTENDED(ca) && ca->ca_clear)
464 continue;
465 if (!ca->ca_scheme[0] || !su_casematch(ca->ca_scheme, d->ca_scheme))
466 continue;
467 if (!ca->ca_realm || !su_strmatch(ca->ca_realm, d->ca_realm))
468 continue;
469
470 if (!(AUTH_CLIENT_IS_EXTENDED(d) && d->ca_clear) &&
471 su_strmatch(d->ca_user, ca->ca_user) &&
472 su_strmatch(d->ca_pass, ca->ca_pass)) {
473 retval++;
474 break;
475 }
476
477 u = su_strdup(d->ca_home, ca->ca_user);
478 p = su_strdup(d->ca_home, ca->ca_pass);
479 if (!u || !p)
480 return -1;
481
482 if (d->ca_user) su_free(d->ca_home, (void *)d->ca_user);
483 if (d->ca_pass) su_free(d->ca_home, (void *)d->ca_pass);
484 d->ca_user = u, d->ca_pass = p;
485 if (AUTH_CLIENT_IS_EXTENDED(d))
486 d->ca_clear = 0;
487
488 retval++;
489 break;
490 }
491 }
492
493 return retval;
494 }
495
496
497 /**Clear authentication data from the authenticator.
498 *
499 * The function auc_clear_credentials() is used to remove the credentials
500 * from the authenticators.
501 *
502 * @param[in,out] auc_list list of authenticators
503 * @param[in] scheme scheme (if non-null, remove only matching credentials)
504 * @param[in] realm realm (if non-null, remove only matching credentials)
505 *
506 * @retval 0 when successful
507 * @retval -1 upon an error
508 */
auc_clear_credentials(auth_client_t ** auc_list,char const * scheme,char const * realm)509 int auc_clear_credentials(auth_client_t **auc_list,
510 char const *scheme,
511 char const *realm)
512 {
513 int retval = 0;
514 int match;
515
516 for (; *auc_list; auc_list = &(*auc_list)->ca_next) {
517 auth_client_t *ca = *auc_list;
518
519 if (!AUTH_CLIENT_IS_EXTENDED(ca))
520 continue;
521
522 if ((scheme != NULL && !su_casematch(scheme, ca->ca_scheme)) ||
523 (realm != NULL && !su_strmatch(realm, ca->ca_realm)))
524 continue;
525
526 match = ca->ca_auc->auc_clear(*auc_list);
527
528 if (match < 0) {
529 retval = -1;
530 break;
531 }
532 if (match)
533 retval++;
534 }
535
536 return retval;
537 }
538
539 static
ca_clear_credentials(auth_client_t * ca)540 int ca_clear_credentials(auth_client_t *ca)
541 {
542 assert(ca); assert(ca->ca_home->suh_size >= (int)(sizeof *ca));
543
544 if (!ca)
545 return -1;
546
547 ca->ca_clear = 1;
548
549 return 1;
550 }
551
552 /** Check if there are credentials for all challenges.
553 *
554 * @retval 1 when authorization can proceed
555 * @retval 0 when there is not enough credentials
556 *
557 * @NEW_1_12_5.
558 */
auc_has_authorization(auth_client_t ** auc_list)559 int auc_has_authorization(auth_client_t **auc_list)
560 {
561 auth_client_t const *ca, *other;
562
563 if (auc_list == NULL)
564 return 0;
565
566 for (ca = *auc_list; ca; ca = ca->ca_next) {
567 if (!ca_has_authorization(ca)) {
568 /*
569 * Check if we have another challenge with same realm but different
570 * scheme
571 */
572 for (other = *auc_list; other; other = other->ca_next) {
573 if (ca == other) {
574 continue;
575 }
576
577 if (ca->ca_credential_class == other->ca_credential_class &&
578 su_strcmp(ca->ca_realm, other->ca_realm) == 0 &&
579 ca_has_authorization(other))
580 break;
581 }
582
583 if (!other)
584 return 0;
585 }
586 }
587
588 return 1;
589 }
590
591 static int
ca_has_authorization(auth_client_t const * ca)592 ca_has_authorization(auth_client_t const *ca)
593 {
594 return ca->ca_credential_class &&
595 ca->ca_auc &&
596 ca->ca_user && ca->ca_pass &&
597 !(AUTH_CLIENT_IS_EXTENDED(ca) && ca->ca_clear);
598 }
599
600 /**Authorize a request.
601 *
602 * The function auc_authorization() is used to add correct authentication
603 * headers to a request. The authentication headers will contain the
604 * credentials generated by the list of authenticators.
605 *
606 * @param[in,out] auc_list list of authenticators
607 * @param[out] msg message to be authenticated
608 * @param[out] pub headers of the message
609 * @param[in] method request method
610 * @param[in] url request URI
611 * @param[in] body message body (NULL if empty)
612 *
613 * @retval 1 when successful
614 * @retval 0 when there is not enough credentials
615 * @retval -1 upon an error
616 */
auc_authorization(auth_client_t ** auc_list,msg_t * msg,msg_pub_t * pub,char const * method,url_t const * url,msg_payload_t const * body)617 int auc_authorization(auth_client_t **auc_list, msg_t *msg, msg_pub_t *pub,
618 char const *method,
619 url_t const *url,
620 msg_payload_t const *body)
621 {
622 auth_client_t *ca;
623 msg_mclass_t const *mc = msg_mclass(msg);
624
625 if (auc_list == NULL || msg == NULL)
626 return -1;
627
628 if (!auc_has_authorization(auc_list))
629 return 0;
630
631 if (pub == NULL)
632 pub = msg_object(msg);
633
634 /* Remove existing credential headers */
635 for (ca = *auc_list; ca; ca = ca->ca_next) {
636 msg_header_t **hh = msg_hclass_offset(mc, pub, ca->ca_credential_class);
637
638 while (hh && *hh)
639 msg_header_remove(msg, pub, *hh);
640 }
641
642 /* Insert new credential headers */
643 for (; *auc_list; auc_list = &(*auc_list)->ca_next) {
644 su_home_t *home = msg_home(msg);
645 msg_header_t *h = NULL;
646
647 ca = *auc_list;
648
649 if (!ca->ca_auc)
650 continue;
651 if (!ca_has_authorization(ca))
652 continue;
653
654 if (ca->ca_auc->auc_authorize(ca, home, method, url, body, &h) < 0)
655 return -1;
656 if (h == NULL)
657 continue;
658 if (msg_header_insert(msg, pub, h) < 0)
659 return -1;
660 }
661
662 return 1;
663 }
664
665 /**Generate headers authorizing a request.
666 *
667 * The function auc_authorization_headers() is used to generate
668 * authentication headers for a request. The list of authentication headers
669 * will contain the credentials generated by the list of authenticators.
670 *
671 * @param[in] auc_list list of authenticators
672 * @param[in] home memory home used to allocate headers
673 * @param[in] method request method
674 * @param[in] url request URI
675 * @param[in] body message body (NULL if empty)
676 * @param[out] return_headers authorization headers return value
677 *
678 * @retval 1 when successful
679 * @retval 0 when there is not enough credentials
680 * @retval -1 upon an error
681 */
auc_authorization_headers(auth_client_t ** auc_list,su_home_t * home,char const * method,url_t const * url,msg_payload_t const * body,msg_header_t ** return_headers)682 int auc_authorization_headers(auth_client_t **auc_list,
683 su_home_t *home,
684 char const *method,
685 url_t const *url,
686 msg_payload_t const *body,
687 msg_header_t **return_headers)
688 {
689 auth_client_t *ca;
690
691 /* Make sure every challenge has credentials */
692 if (!auc_has_authorization(auc_list))
693 return 0;
694
695 /* Create new credential headers */
696 for (; *auc_list; auc_list = &(*auc_list)->ca_next) {
697 msg_header_t *h = NULL;
698
699 ca = *auc_list;
700
701 if (!ca->ca_auc)
702 continue;
703 if (!ca_has_authorization(ca))
704 continue;
705
706 if (ca->ca_auc->auc_authorize(ca, home, method, url, body, &h) < 0)
707 return -1;
708
709 *return_headers = h;
710
711 while (*return_headers)
712 return_headers = &(*return_headers)->sh_next;
713 }
714
715 return 1;
716 }
717
718 /* ---------------------------------------------------------------------- */
719 /* Basic scheme */
720
721 static int auc_basic_authorization(auth_client_t *ca,
722 su_home_t *h,
723 char const *method,
724 url_t const *url,
725 msg_payload_t const *body,
726 msg_header_t **);
727
728 static const auth_client_plugin_t ca_basic_plugin =
729 {
730 /* auc_plugin_size: */ sizeof ca_basic_plugin,
731 /* auc_size: */ sizeof (auth_client_t),
732 /* auc_name: */ "Basic",
733 /* auc_challenge: */ NULL,
734 /* auc_authorize: */ auc_basic_authorization,
735 /* auc_info: */ NULL,
736 /* auc_clear: */ ca_clear_credentials
737 };
738
739 /**Create a basic authorization header.
740 *
741 * The function auc_basic_authorization() creates a basic authorization
742 * header from username @a user and password @a pass. The authorization
743 * header type is determined by @a hc - it can be sip_authorization_class,
744 * sip_proxy_authorization_class, http_authorization_class, or
745 * http_proxy_authorization_class, for instance.
746 *
747 * @param home memory home used to allocate memory for the new header
748 * @param hc header class for the header to be created
749 * @param user user name
750 * @param pass password
751 *
752 * @return
753 * The function auc_basic_authorization() returns a pointer to newly created
754 * authorization header, or NULL upon an error.
755 */
auc_basic_authorization(auth_client_t * ca,su_home_t * home,char const * method,url_t const * url,msg_payload_t const * body,msg_header_t ** return_headers)756 int auc_basic_authorization(auth_client_t *ca,
757 su_home_t *home,
758 char const *method,
759 url_t const *url,
760 msg_payload_t const *body,
761 msg_header_t **return_headers)
762 {
763 msg_hclass_t *hc = ca->ca_credential_class;
764 char const *user = ca->ca_user;
765 char const *pass = ca->ca_pass;
766 size_t ulen, plen, uplen, b64len, basiclen;
767 char *basic, *base64, *userpass;
768 char buffer[71];
769
770 if (user == NULL || pass == NULL)
771 return -1;
772
773 if (AUTH_CLIENT_IS_EXTENDED(ca) && ca->ca_clear)
774 return 0;
775
776 ulen = strlen(user), plen = strlen(pass), uplen = ulen + 1 + plen;
777 b64len = BASE64_SIZE(uplen);
778 basiclen = strlen("Basic ") + b64len;
779
780 if (sizeof(buffer) > basiclen + 1)
781 basic = buffer;
782 else
783 basic = malloc(basiclen + 1);
784
785 if (basic == NULL)
786 return -1;
787
788 /*
789 * Basic authentication consists of username and password separated by
790 * colon and then base64 encoded.
791 */
792 strcpy(basic, "Basic ");
793 base64 = basic + strlen("Basic ");
794 userpass = base64 + b64len - uplen;
795 memcpy(userpass, user, ulen);
796 userpass[ulen] = ':';
797 memcpy(userpass + ulen + 1, pass, plen);
798 userpass[uplen] = '\0';
799
800 base64_e(base64, b64len + 1, userpass, uplen);
801
802 base64[b64len] = '\0';
803
804 *return_headers = msg_header_make(home, hc, basic);
805
806 if (buffer != basic)
807 free(basic);
808
809 return *return_headers ? 0 : -1;
810 }
811
812 /* ---------------------------------------------------------------------- */
813 /* Digest scheme */
814
815 typedef struct auth_digest_client_s
816 {
817 auth_client_t cda_client;
818
819 int cda_ncount;
820 char const *cda_cnonce;
821 auth_challenge_t cda_ac[1];
822 } auth_digest_client_t;
823
824 static int auc_digest_challenge(auth_client_t *ca,
825 msg_auth_t const *ch);
826 static int auc_digest_authorization(auth_client_t *ca,
827 su_home_t *h,
828 char const *method,
829 url_t const *url,
830 msg_payload_t const *body,
831 msg_header_t **);
832 static int auc_digest_info(auth_client_t *ca,
833 msg_auth_info_t const *info);
834
835 static const auth_client_plugin_t ca_digest_plugin =
836 {
837 /* auc_plugin_size: */ sizeof ca_digest_plugin,
838 /* auc_size: */ sizeof (auth_digest_client_t),
839 /* auc_name: */ "Digest",
840 /* auc_challenge: */ auc_digest_challenge,
841 /* auc_authorize: */ auc_digest_authorization,
842 /* auc_info: */ auc_digest_info,
843 /* auc_clear: */ ca_clear_credentials
844 };
845
846 /** Store a digest authorization challenge.
847 *
848 * @retval 2 if credentials need to be (re)sent
849 * @retval 1 if challenge was updated
850 * @retval -1 upon an error
851 */
auc_digest_challenge(auth_client_t * ca,msg_auth_t const * ch)852 static int auc_digest_challenge(auth_client_t *ca, msg_auth_t const *ch)
853 {
854 su_home_t *home = ca->ca_home;
855 auth_digest_client_t *cda = (auth_digest_client_t *)ca;
856 auth_challenge_t ac[1] = {{ sizeof ac }};
857 int stale;
858
859 if (auth_digest_challenge_get(home, ac, ch->au_params) < 0)
860 goto error;
861
862 /* Check that we can handle the challenge */
863 if (!ac->ac_md5 && !ac->ac_md5sess && !ac->ac_sha256 && !ac->ac_sha256sess && !ac->ac_sha512_256 && !ac->ac_sha512_256sess)
864 goto error;
865 if (ac->ac_qop && !ac->ac_auth && !ac->ac_auth_int)
866 goto error;
867
868 stale = ac->ac_stale || cda->cda_ac->ac_nonce == NULL;
869
870 if (ac->ac_qop && (cda->cda_cnonce == NULL || ac->ac_stale || ca->ca_clear )) {
871 su_guid_t guid[1];
872 char *cnonce;
873 size_t b64len = BASE64_MINSIZE(sizeof(guid)) + 1;
874 if (cda->cda_cnonce != NULL)
875 /* Free the old one if we are updating after stale=true */
876 su_free(home, (void *)cda->cda_cnonce);
877 su_guid_generate(guid);
878 cda->cda_cnonce = cnonce = su_alloc(home, b64len);
879 base64_e(cnonce, b64len, guid, sizeof(guid));
880 cda->cda_ncount = 0;
881 }
882
883 auth_digest_challenge_free_params(home, cda->cda_ac);
884
885 *cda->cda_ac = *ac;
886
887 return stale ? 2 : 1;
888
889 error:
890 auth_digest_challenge_free_params(home, ac);
891 return -1;
892 }
893
auc_digest_info(auth_client_t * ca,msg_auth_info_t const * info)894 static int auc_digest_info(auth_client_t *ca,
895 msg_auth_info_t const *info)
896 {
897 auth_digest_client_t *cda = (auth_digest_client_t *)ca;
898 su_home_t *home = ca->ca_home;
899 char const *nextnonce = NULL;
900 issize_t n;
901
902 n = auth_get_params(home, info->ai_params,
903 "nextnonce=", &nextnonce,
904 NULL);
905
906 if (n <= 0)
907 return n;
908
909 cda->cda_ac->ac_nonce = nextnonce;
910
911 return 1;
912 }
913
914 /**Create a digest authorization header.
915 *
916 * Creates a digest authorization header from username @a user and password
917 * @a pass, client nonce @a cnonce, client nonce count @a nc, request method
918 * @a method, request URI @a uri and message body @a data. The authorization
919 * header type is determined by @a hc - it can be either
920 * sip_authorization_class or sip_proxy_authorization_class, as well as
921 * http_authorization_class or http_proxy_authorization_class.
922 *
923 * @retval 1 when authorization headers has been created
924 * @retval 0 when there is no credentials
925 * @retval -1 upon an error
926 */
927 static
auc_digest_authorization(auth_client_t * ca,su_home_t * home,char const * method,url_t const * url,msg_payload_t const * body,msg_header_t ** return_headers)928 int auc_digest_authorization(auth_client_t *ca,
929 su_home_t *home,
930 char const *method,
931 url_t const *url,
932 msg_payload_t const *body,
933 msg_header_t **return_headers)
934 {
935 auth_digest_client_t *cda = (auth_digest_client_t *)ca;
936 msg_hclass_t *hc = ca->ca_credential_class;
937 char const *user = ca->ca_user;
938 char const *pass = ca->ca_pass;
939 auth_challenge_t const *ac = cda->cda_ac;
940 char const *cnonce = cda->cda_cnonce;
941 unsigned nc = ++cda->cda_ncount;
942 void const *data = body ? body->pl_data : "";
943 usize_t dlen = body ? body->pl_len : 0;
944 char *uri;
945
946 msg_header_t *h;
947 auth_hexmd5_t sessionkey, response;
948 auth_response_t ar[1] = {{ 0 }};
949 char ncount[17];
950
951 if (!user || !pass || (AUTH_CLIENT_IS_EXTENDED(ca) && ca->ca_clear))
952 return 0;
953
954 ar->ar_size = sizeof(ar);
955 ar->ar_username = user;
956 ar->ar_realm = ac->ac_realm;
957 ar->ar_nonce = ac->ac_nonce;
958 ar->ar_algorithm = NULL;
959 ar->ar_md5 = ac->ac_md5;
960 ar->ar_md5sess = ac->ac_md5sess;
961 ar->ar_opaque = ac->ac_opaque;
962 ar->ar_qop = NULL;
963 ar->ar_auth = ac->ac_auth;
964 ar->ar_auth_int = ac->ac_auth_int;
965 ar->ar_uri = uri = url_as_string(home, url);
966
967 if (ar->ar_uri == NULL)
968 return -1;
969
970 /* If there is no qop, we MUST NOT include cnonce or nc */
971 if (!ar->ar_auth && !ar->ar_auth_int)
972 cnonce = NULL;
973
974 if (cnonce) {
975 snprintf(ncount, sizeof(ncount), "%08x", nc);
976 ar->ar_cnonce = cnonce;
977 ar->ar_nc = ncount;
978 }
979
980 auth_digest_sessionkey(ar, sessionkey, pass);
981 auth_digest_response(ar, response, sessionkey, method, data, dlen);
982
983 h = msg_header_format(home, hc,
984 "Digest "
985 "username=\"%s\", "
986 "realm=\"%s\", "
987 "nonce=\"%s"
988 "%s%s"
989 "%s%s"
990 "%s%s, "
991 "uri=\"%s\", "
992 "response=\"%s\""
993 "%s%s"
994 "%s%s",
995 ar->ar_username,
996 ar->ar_realm,
997 ar->ar_nonce,
998 cnonce ? "\", cnonce=\"" : "",
999 cnonce ? cnonce : "",
1000 ar->ar_opaque ? "\", opaque=\"" : "",
1001 ar->ar_opaque ? ar->ar_opaque : "",
1002 ar->ar_algorithm ? "\", algorithm=" : "",
1003 ar->ar_algorithm ? ar->ar_algorithm : "",
1004 ar->ar_uri,
1005 response,
1006 ar->ar_auth || ar->ar_auth_int ? ", qop=" : "",
1007 ar->ar_auth_int ? "auth-int" :
1008 (ar->ar_auth ? "auth" : ""),
1009 cnonce ? ", nc=" : "",
1010 cnonce ? ncount : "");
1011
1012 su_free(home, uri);
1013
1014 if (!h)
1015 return -1;
1016 *return_headers = h;
1017 return 0;
1018 }
1019
1020
1021 /* ---------------------------------------------------------------------- */
1022
1023 #define MAX_AUC 20
1024
1025 static auth_client_plugin_t const *ca_plugins[MAX_AUC] =
1026 {
1027 &ca_digest_plugin, &ca_basic_plugin, NULL
1028 };
1029
1030 /** Register an authentication client plugin */
auc_register_plugin(auth_client_plugin_t const * plugin)1031 int auc_register_plugin(auth_client_plugin_t const *plugin)
1032 {
1033 int i;
1034
1035 if (plugin == NULL ||
1036 plugin->auc_name == NULL ||
1037 plugin->auc_authorize == NULL)
1038 return errno = EFAULT, -1;
1039
1040 if (plugin->auc_size < (int) sizeof (auth_client_t))
1041 return errno = EINVAL, -1;
1042
1043 for (i = 0; i < MAX_AUC; i++) {
1044 if (ca_plugins[i] == NULL ||
1045 su_strmatch(plugin->auc_name, ca_plugins[i]->auc_name) == 0) {
1046 ca_plugins[i] = plugin;
1047 return 0;
1048 }
1049 }
1050
1051 return errno = ENOMEM, -1;
1052 }
1053
1054 /** Allocate an (possibly extended) auth_client_t structure. */
1055 static
ca_create(su_home_t * home,char const * scheme,char const * realm)1056 auth_client_t *ca_create(su_home_t *home,
1057 char const *scheme,
1058 char const *realm)
1059 {
1060 auth_client_plugin_t const *auc = NULL;
1061 auth_client_t *ca;
1062 size_t aucsize = (sizeof *ca), realmlen, size;
1063 char *s;
1064 int i;
1065
1066 if (scheme == NULL || realm == NULL)
1067 return (void)(errno = EFAULT), NULL;
1068
1069 realmlen = strlen(realm) + 1;
1070
1071 for (i = 0; i < MAX_AUC; i++) {
1072 auc = ca_plugins[i];
1073 if (!auc || su_casematch(auc->auc_name, scheme))
1074 break;
1075 }
1076
1077 /* XXX - should report error if the auth scheme is not known? */
1078
1079 aucsize = auc ? (size_t)auc->auc_size : (sizeof *ca);
1080 size = aucsize + realmlen;
1081 if (!auc)
1082 size += strlen(scheme) + 1;
1083
1084 ca = su_home_clone(home, (isize_t)size);
1085 if (!ca)
1086 return ca;
1087
1088 s = (char *)ca + aucsize;
1089 ca->ca_auc = auc;
1090 ca->ca_realm = strcpy(s, realm);
1091 ca->ca_scheme = auc ? auc->auc_name : strcpy(s + realmlen, scheme);
1092
1093 return ca;
1094 }
1095
ca_destroy(su_home_t * home,auth_client_t * ca)1096 void ca_destroy(su_home_t *home, auth_client_t *ca)
1097 {
1098 su_free(home, ca);
1099 }
1100
1101
1102 #if HAVE_SOFIA_SIP
1103 #include <sofia-sip/sip.h>
1104
1105 /**Authorize a SIP request.
1106 *
1107 * The function auc_authorize() is used to add correct authentication
1108 * headers to a SIP request. The authentication headers will contain the
1109 * credentials generated by the list of authenticators.
1110 *
1111 * @param[in,out] auc_list list of authenticators
1112 * @param[in,out] msg message to be authenticated
1113 * @param[in,out] sip sip headers of the message
1114 *
1115 * @retval 1 when successful
1116 * @retval 0 when there is not enough credentials
1117 * @retval -1 upon an error
1118 */
auc_authorize(auth_client_t ** auc_list,msg_t * msg,sip_t * sip)1119 int auc_authorize(auth_client_t **auc_list, msg_t *msg, sip_t *sip)
1120 {
1121 sip_request_t *rq = sip ? sip->sip_request : NULL;
1122
1123 if (!rq)
1124 return 0;
1125
1126 return auc_authorization(auc_list, msg, (msg_pub_t *)sip,
1127 rq->rq_method_name,
1128 /*
1129 RFC 3261 defines the protection domain based
1130 only on realm, so we do not bother get a
1131 correct URI to auth module.
1132 */
1133 rq->rq_url,
1134 sip->sip_payload);
1135 }
1136 #endif
1137