1 /*
2     Copyright (C) 2002-2008  Thomas Ries <tries@gmx.net>
3 
4     This file is part of Siproxd.
5 
6     Siproxd is free software; you can redistribute it and/or modify
7     it under the terms of the GNU General Public License as published by
8     the Free Software Foundation; either version 2 of the License, or
9     (at your option) any later version.
10 
11     Siproxd is distributed in the hope that it will be useful,
12     but WITHOUT ANY WARRANTY; without even the implied warranty of
13     MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
14     GNU General Public License for more details.
15 
16     You should have received a copy of the GNU General Public License
17     along with Siproxd; if not, write to the Free Software
18     Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA  02111-1307  USA
19 */
20 
21 #include "config.h"
22 
23 #include <stdio.h>
24 #include <errno.h>
25 #include <stdlib.h>
26 #include <string.h>
27 
28 #include <sys/time.h>
29 
30 #include <netinet/in.h>
31 
32 #include <osipparser2/osip_parser.h>
33 #include <osipparser2/osip_md5.h>
34 
35 #include "digcalc.h"
36 
37 #include "siproxd.h"
38 #include "log.h"
39 
40 static char const ident[]="$Id: auth.c 392 2008-07-22 11:17:49Z hb9xar $";
41 
42 /* configuration storage */
43 extern struct siproxd_config configuration;
44 
45 /* Global File instance on pw file */
46 extern FILE *siproxd_passwordfile;
47 
48 /* local protorypes */
49 static char *auth_generate_nonce(void);
50 static int auth_check(osip_proxy_authorization_t *proxy_auth);
51 static char *auth_getpwd(char *username);
52 
53 /*
54  * perform proxy authentication
55  *
56  * RETURNS
57  *	STS_SUCCESS : authentication ok / not needed
58  *	STS_FAILURE : authentication failed
59  *	STS_NEEDAUTH: authentication needed
60  */
authenticate_proxy(osip_message_t * sipmsg)61 int authenticate_proxy(osip_message_t *sipmsg) {
62    osip_proxy_authorization_t *proxy_auth=NULL;
63 
64    /* required by config? */
65    if (configuration.proxy_auth_realm == NULL) {
66       return STS_SUCCESS;
67    }
68 
69    /* supplied by UA? */
70    osip_message_get_proxy_authorization(sipmsg, 0, &proxy_auth);
71    if (proxy_auth == NULL) {
72       DEBUGC(DBCLASS_AUTH,"proxy-auth required, not supplied by UA");
73       return STS_NEED_AUTH;
74    }
75 
76    /* verify supplied authentication */
77    if (auth_check(proxy_auth) == STS_SUCCESS) {
78       DEBUGC(DBCLASS_AUTH,"proxy-auth succeeded");
79       return STS_SUCCESS;
80    }
81 
82    /* authentication failed */
83    WARN("authenticate_proxy failed");
84    return STS_FAILURE;
85 }
86 
87 /*
88  * includes proxy authentication header in SIP message
89  *
90  * RETURNS
91  *	STS_SUCCESS
92  *	STS_FAILURE
93  */
auth_include_authrq(osip_message_t * sipmsg)94 int auth_include_authrq(osip_message_t *sipmsg) {
95    osip_proxy_authenticate_t *p_auth;
96    char *realm=NULL;
97 
98    if (osip_proxy_authenticate_init(&p_auth) != 0) {
99       ERROR("proxy_authenticate_init failed");
100       return STS_FAILURE;
101    }
102 
103    osip_proxy_authenticate_set_auth_type(p_auth, osip_strdup("Digest"));
104    osip_proxy_authenticate_set_nonce(p_auth, osip_strdup(auth_generate_nonce()));
105    realm=osip_malloc(strlen(configuration.proxy_auth_realm)+3); /* add 2x" and \0 */
106    if (realm) {
107       sprintf(realm,"\"%s\"",configuration.proxy_auth_realm);
108       osip_proxy_authenticate_set_realm(p_auth, realm);
109    } else {
110       ERROR("unable to malloc() %ld bytes for authentication realm",
111             (long)strlen(configuration.proxy_auth_realm)+3);
112       return STS_FAILURE;
113    }
114 
115    osip_list_add (&(sipmsg->proxy_authenticates), p_auth, -1);
116 
117    DEBUGC(DBCLASS_AUTH,"added authentication header");
118 
119    return STS_SUCCESS;
120 }
121 
122 /*
123  * generates a nonce string
124  *
125  * RETURNS nonce string
126  */
auth_generate_nonce()127 static char *auth_generate_nonce() {
128    static char nonce[40];
129    struct timeval tv;
130 
131    gettimeofday (&tv, NULL);
132 
133 /* yeah, I know... should be a better algorithm */
134 /* enclose it in double quotes, as libosip does *not* do it (2.0.6) */
135    sprintf(nonce, "\"%8.8lx%8.8lx%8.8x%8.8x\"",
136            (long)tv.tv_sec, (long)tv.tv_usec, rand(), rand() );
137 
138    DEBUGC(DBCLASS_AUTH,"created nonce=\"%s\"",nonce);
139    return nonce;
140 }
141 
142 
143 /*
144  * verify the supplied authentication information from UA
145  *
146  * RETURNS
147  *	STS_SUCCESS if succeeded
148  *	STS_FAILURE if failed
149  */
auth_check(osip_proxy_authorization_t * proxy_auth)150 static int auth_check(osip_proxy_authorization_t *proxy_auth) {
151    char *password=NULL;
152    int sts;
153 
154    HASHHEX HA1;
155    HASHHEX HA2 = "";
156    HASHHEX Lcl_Response;
157 
158    char *Username   = NULL;
159    char *Realm      = NULL;
160    char *Nonce      = NULL;
161    char *CNonce     = NULL;
162    char *NonceCount = NULL;
163    char *Qpop	    = NULL;
164    char *Uri	    = NULL;
165    char *Response   = NULL;
166 
167    /* if item exists, allocate& copy string without quotes */
168    if (proxy_auth->username)
169       Username=osip_strdup_without_quote(proxy_auth->username);
170 
171    if (proxy_auth->realm)
172       Realm=osip_strdup_without_quote(proxy_auth->realm);
173 
174    if (proxy_auth->nonce)
175       Nonce=osip_strdup_without_quote(proxy_auth->nonce);
176 
177    if (proxy_auth->cnonce)
178       CNonce=osip_strdup_without_quote(proxy_auth->cnonce);
179 
180    if (proxy_auth->nonce_count)
181       NonceCount=osip_strdup_without_quote(proxy_auth->nonce_count);
182 
183    if (proxy_auth->message_qop)
184       Qpop=osip_strdup_without_quote(proxy_auth->message_qop);
185 
186    if (proxy_auth->uri)
187       Uri=osip_strdup_without_quote(proxy_auth->uri);
188 
189    if (proxy_auth->response)
190       Response=osip_strdup_without_quote(proxy_auth->response);
191 
192    /* get password */
193    if (configuration.proxy_auth_pwfile) {
194       /* check in passwd file */
195       password=auth_getpwd(Username);
196    } else if (configuration.proxy_auth_passwd) {
197       /* get password from configuration */
198       password=configuration.proxy_auth_passwd;
199    }
200 
201    if (password == NULL) {
202       DEBUGC(DBCLASS_AUTH,"user [%s] not in password file!",
203              (Username)?Username:"*NULL*");
204       return STS_FAILURE;
205    }
206 
207    DEBUGC(DBCLASS_BABBLE," username=\"%s\"",Username  );
208    DEBUGC(DBCLASS_BABBLE," realm   =\"%s\"",Realm     );
209    DEBUGC(DBCLASS_BABBLE," nonce   =\"%s\"",Nonce     );
210    DEBUGC(DBCLASS_BABBLE," cnonce  =\"%s\"",CNonce    );
211    DEBUGC(DBCLASS_BABBLE," nonce_nc=\"%s\"",NonceCount);
212    DEBUGC(DBCLASS_BABBLE," qpop    =\"%s\"",Qpop      );
213    DEBUGC(DBCLASS_BABBLE," uri     =\"%s\"",Uri	    );
214    DEBUGC(DBCLASS_BABBLE," response=\"%s\"",Response  );
215 
216    /* calculate the MD5 digest (heavily inspired from linphone code) */
217    DigestCalcHA1("MD5", Username, Realm, password, Nonce, CNonce, HA1);
218    DigestCalcResponse(HA1, Nonce, NonceCount, CNonce, Qpop,
219 		      "REGISTER", Uri, HA2, Lcl_Response);
220 
221    DEBUGC(DBCLASS_BABBLE,"calculated Response=\"%s\"", Lcl_Response);
222 
223    if (strcmp((char*)Lcl_Response, Response)==0) {
224       DEBUGC(DBCLASS_AUTH,"Authentication succeeded");
225       sts = STS_SUCCESS;
226    } else {
227       DEBUGC(DBCLASS_AUTH,"Authentication failed");
228       sts = STS_FAILURE;
229    }
230 
231    /* free allocated memory from above */
232    if (Username)   free(Username);
233    if (Realm)      free(Realm);
234    if (Nonce)      free(Nonce);
235    if (CNonce)     free(CNonce);
236    if (NonceCount) free(NonceCount);
237    if (Qpop)       free(Qpop);
238    if (Uri)        free(Uri);
239    if (Response)   free(Response);
240 
241    return sts;
242 }
243 
244 
245 /*
246  * lookup in the password file and return
247  * the user specific password for 'username'
248  *
249  * RETURNS
250  *	password for user or NULL if not found
251  */
auth_getpwd(char * username)252 static char *auth_getpwd(char *username) {
253    typedef struct {
254       char username[USERNAME_SIZE];
255       char password[PASSWORD_SIZE];
256    } auth_cache_t;
257 
258    char buff[128];
259    int i;
260    static auth_cache_t *auth_cache=NULL;
261    void *tmpptr;
262    static int auth_cache_size=0;
263    static int auth_cache_count=0;
264 
265    if (auth_cache==NULL) {
266       DEBUGC(DBCLASS_AUTH,"initialize password cache");
267 
268       /* config file not found or unable to open for read */
269       if (siproxd_passwordfile==NULL) {
270          ERROR ("could not open password file: %s", strerror(errno));
271          return NULL;
272       }
273 
274       rewind(siproxd_passwordfile);
275 
276       while (fgets(buff,sizeof(buff),siproxd_passwordfile) != NULL) {
277          /* life insurance */
278          buff[sizeof(buff)-1]='\0';
279 
280 	 /* strip newline if present */
281 	 if (buff[strlen(buff)-1]=='\n') buff[strlen(buff)-1]='\0';
282 
283 	 /* strip emty lines */
284 	 if (strlen(buff) == 0) continue;
285 
286 	 /* strip comments and line with only whitespaces */
287 	 for (i=0;i<strlen(buff);i++) {
288             if ((buff[i] == ' ') || (buff[i] == '\t')) continue;
289             if (buff[i] =='#') i=strlen(buff);
290             break;
291 	 }
292 	 if (i == strlen(buff)) continue;
293 
294          /* allocate space whenever needed */
295 	 if (auth_cache_count >= auth_cache_size) {
296 	    auth_cache_size+=10;
297 	    tmpptr=realloc(auth_cache, auth_cache_size*sizeof(auth_cache_t));
298             if (tmpptr != NULL) {
299                auth_cache= (auth_cache_t *)tmpptr;
300 	    } else {
301                ERROR("realloc failed! this is not good");
302 	       auth_cache_size-=10;
303 	       return NULL;
304 	    }
305          } /* cnt > size */
306 
307          i=sscanf(buff,"%s %s",auth_cache[auth_cache_count].username,
308 	                       auth_cache[auth_cache_count].password);
309          /* if I got username & passwd, make it valid and increment counter */
310          if (i == 2) auth_cache_count++;
311       }
312 
313    } /* initialize cache */
314 
315    /* search cache for user */
316    DEBUGC(DBCLASS_AUTH,"searching password entry for user %s",username);
317    for (i=0; i< auth_cache_count;i++) {
318       if (strcmp(username, auth_cache[i].username)==0) {
319          DEBUGC(DBCLASS_AUTH,"found password entry for user %s",username);
320          return auth_cache[i].password;
321       }
322    }
323 
324    DEBUGC(DBCLASS_AUTH,"no password entry found for user %s",username);
325    return NULL;
326 }
327 
328 
329 /*-------------------------------------------------------------------------
330   -------------------------------------------------------------------------
331   The routines below have been taken from linphone
332   (osipua/src/authentication.c)
333   -------------------------------------------------------------------------
334   -------------------------------------------------------------------------*/
335 
CvtHex(IN HASH Bin,OUT HASHHEX Hex)336 void CvtHex(
337 	    IN HASH Bin,
338 	    OUT HASHHEX Hex
339 	    )
340 {
341   unsigned short i;
342   unsigned char j;
343 
344   for (i = 0; i < HASHLEN; i++) {
345     j = (Bin[i] >> 4) & 0xf;
346     if (j <= 9)
347       Hex[i*2] = (j + '0');
348     else
349       Hex[i*2] = (j + 'a' - 10);
350     j = Bin[i] & 0xf;
351     if (j <= 9)
352       Hex[i*2+1] = (j + '0');
353     else
354       Hex[i*2+1] = (j + 'a' - 10);
355   };
356   Hex[HASHHEXLEN] = '\0';
357 }
358 
359 /* calculate H(A1) as per spec */
DigestCalcHA1(IN char * pszAlg,IN char * pszUserName,IN char * pszRealm,IN char * pszPassword,IN char * pszNonce,IN char * pszCNonce,OUT HASHHEX SessionKey)360 void DigestCalcHA1(
361 		   IN char * pszAlg,
362 		   IN char * pszUserName,
363 		   IN char * pszRealm,
364 		   IN char * pszPassword,
365 		   IN char * pszNonce,
366 		   IN char * pszCNonce,
367 		   OUT HASHHEX SessionKey
368 		   )
369 {
370   osip_MD5_CTX Md5Ctx;
371   HASH HA1;
372 
373   osip_MD5Init(&Md5Ctx);
374   if (pszUserName) osip_MD5Update(&Md5Ctx, (unsigned char*)pszUserName,
375                                   strlen(pszUserName));
376   osip_MD5Update(&Md5Ctx, (unsigned char*)":", 1);
377   if (pszRealm)    osip_MD5Update(&Md5Ctx, (unsigned char*)pszRealm,
378                                   strlen(pszRealm));
379   osip_MD5Update(&Md5Ctx, (unsigned char*)":", 1);
380   if (pszPassword) osip_MD5Update(&Md5Ctx, (unsigned char*)pszPassword,
381                                   strlen(pszPassword));
382   osip_MD5Final(HA1, &Md5Ctx);
383 
384   if ((pszAlg!=NULL) && (osip_strcasecmp(pszAlg, "md5-sess") == 0)) {
385     osip_MD5Init(&Md5Ctx);
386     osip_MD5Update(&Md5Ctx, HA1, HASHLEN);
387     osip_MD5Update(&Md5Ctx, (unsigned char*)":", 1);
388     if (pszNonce)  osip_MD5Update(&Md5Ctx, (unsigned char*)pszNonce,
389                                   strlen(pszNonce));
390     osip_MD5Update(&Md5Ctx, (unsigned char*)":", 1);
391     if (pszCNonce) osip_MD5Update(&Md5Ctx, (unsigned char*)pszCNonce,
392                                   strlen(pszCNonce));
393     osip_MD5Final(HA1, &Md5Ctx);
394   };
395   CvtHex(HA1, SessionKey);
396 }
397 
398 /* calculate request-digest/response-digest as per HTTP Digest spec */
DigestCalcResponse(IN HASHHEX HA1,IN char * pszNonce,IN char * pszNonceCount,IN char * pszCNonce,IN char * pszQop,IN char * pszMethod,IN char * pszDigestUri,IN HASHHEX HEntity,OUT HASHHEX Response)399 void DigestCalcResponse(
400 			IN HASHHEX HA1,         /* H(A1) */
401 			IN char * pszNonce,     /* nonce from server */
402 			IN char * pszNonceCount,  /* 8 hex digits */
403 			IN char * pszCNonce,    /* client nonce */
404 			IN char * pszQop,       /* qop-value: "", "auth", "auth-int" */
405 			IN char * pszMethod,    /* method from the request */
406 			IN char * pszDigestUri, /* requested URL */
407 			IN HASHHEX HEntity,     /* H(entity body) if qop="auth-int" */
408 			OUT HASHHEX Response    /* request-digest or response-digest */
409 			)
410 {
411   osip_MD5_CTX Md5Ctx;
412   HASH HA2;
413   HASH RespHash;
414   HASHHEX HA2Hex;
415 
416   /* calculate H(A2) */
417   osip_MD5Init(&Md5Ctx);
418   if (pszMethod)    osip_MD5Update(&Md5Ctx, (unsigned char*)pszMethod,
419                                    strlen(pszMethod));
420   osip_MD5Update(&Md5Ctx, (unsigned char*)":", 1);
421   if (pszDigestUri) osip_MD5Update(&Md5Ctx, (unsigned char*)pszDigestUri,
422                                    strlen(pszDigestUri));
423 
424   if (pszQop!=NULL) {
425       goto auth_withqop;
426   };
427 
428   /* auth_withoutqop: */
429   osip_MD5Final(HA2, &Md5Ctx);
430   CvtHex(HA2, HA2Hex);
431 
432   /* calculate response */
433   osip_MD5Init(&Md5Ctx);
434   osip_MD5Update(&Md5Ctx, HA1, HASHHEXLEN);
435   osip_MD5Update(&Md5Ctx, (unsigned char*)":", 1);
436   if (pszNonce)    osip_MD5Update(&Md5Ctx, (unsigned char*)pszNonce, strlen(pszNonce));
437   osip_MD5Update(&Md5Ctx, (unsigned char*)":", 1);
438 
439   goto end;
440 
441  auth_withqop:
442 
443   osip_MD5Update(&Md5Ctx, (unsigned char*)":", 1);
444   osip_MD5Update(&Md5Ctx, HEntity, HASHHEXLEN);
445   osip_MD5Final(HA2, &Md5Ctx);
446   CvtHex(HA2, HA2Hex);
447 
448   /* calculate response */
449   osip_MD5Init(&Md5Ctx);
450   osip_MD5Update(&Md5Ctx, HA1, HASHHEXLEN);
451   osip_MD5Update(&Md5Ctx, (unsigned char*)":", 1);
452   if (pszNonce)    osip_MD5Update(&Md5Ctx, (unsigned char*)pszNonce, strlen(pszNonce));
453   osip_MD5Update(&Md5Ctx, (unsigned char*)":", 1);
454   if (pszNonceCount)osip_MD5Update(&Md5Ctx, (unsigned char*)pszNonceCount, strlen(pszNonceCount));
455   osip_MD5Update(&Md5Ctx, (unsigned char*)":", 1);
456   if (pszCNonce)   osip_MD5Update(&Md5Ctx, (unsigned char*)pszCNonce, strlen(pszCNonce));
457   osip_MD5Update(&Md5Ctx, (unsigned char*)":", 1);
458   if (pszQop)      osip_MD5Update(&Md5Ctx, (unsigned char*)pszQop, strlen(pszQop));
459   osip_MD5Update(&Md5Ctx, (unsigned char*)":", 1);
460 
461  end:
462   osip_MD5Update(&Md5Ctx, HA2Hex, HASHHEXLEN);
463   osip_MD5Final(RespHash, &Md5Ctx);
464   CvtHex(RespHash, Response);
465 }
466 
467 
468