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)
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