Revert "Revert "and added files""
[bcm963xx.git] / userapps / opensource / siproxd / src / auth.c
1 /*
2     Copyright (C) 2002-2005  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,v 1.18 2005/01/08 10:05:12 hb9xar Exp $";
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  */
61 int authenticate_proxy(sip_ticket_t *ticket) {
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(ticket->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  */
94 int auth_include_authrq(sip_ticket_t *ticket) {
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=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() %i bytes for authentication realm",
111             strlen(configuration.proxy_auth_realm)+3);
112       return STS_FAILURE;
113    }
114
115    osip_list_add (ticket->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  */
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  */
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) password="";
202
203    DEBUGC(DBCLASS_BABBLE," username=\"%s\"",Username  );
204    DEBUGC(DBCLASS_BABBLE," realm   =\"%s\"",Realm     );
205    DEBUGC(DBCLASS_BABBLE," nonce   =\"%s\"",Nonce     );
206    DEBUGC(DBCLASS_BABBLE," cnonce  =\"%s\"",CNonce    );
207    DEBUGC(DBCLASS_BABBLE," nonce_nc=\"%s\"",NonceCount);
208    DEBUGC(DBCLASS_BABBLE," qpop    =\"%s\"",Qpop      );
209    DEBUGC(DBCLASS_BABBLE," uri     =\"%s\"",Uri     );
210    DEBUGC(DBCLASS_BABBLE," response=\"%s\"",Response  );
211
212    /* calculate the MD5 digest (heavily inspired from linphone code) */
213    DigestCalcHA1("MD5", Username, Realm, password, Nonce, CNonce, HA1);
214    DigestCalcResponse(HA1, Nonce, NonceCount, CNonce, Qpop,
215                       "REGISTER", Uri, HA2, Lcl_Response);
216
217    DEBUGC(DBCLASS_BABBLE,"calculated Response=\"%s\"", Lcl_Response);
218
219    if (strcmp(Lcl_Response, Response)==0) {
220       DEBUGC(DBCLASS_AUTH,"Authentication succeeded");
221       sts = STS_SUCCESS;
222    } else {
223       DEBUGC(DBCLASS_AUTH,"Authentication failed");
224       sts = STS_FAILURE;
225    }
226
227    /* free allocated memory from above */
228    if (Username)   free(Username);
229    if (Realm)      free(Realm);
230    if (Nonce)      free(Nonce);
231    if (CNonce)     free(CNonce);
232    if (NonceCount) free(NonceCount);
233    if (Qpop)       free(Qpop);
234    if (Uri)        free(Uri);
235    if (Response)   free(Response);
236
237    return sts;
238 }
239
240
241 /*
242  * lookup in the password file and return
243  * the user specific password for 'username'
244  *
245  * RETURNS
246  *      password for user or NULL if not found
247  */
248 static char *auth_getpwd(char *username) {
249    typedef struct {
250       char username[USERNAME_SIZE];
251       char password[PASSWORD_SIZE];
252    } auth_cache_t;
253
254    char buff[128];
255    int i;
256    static auth_cache_t *auth_cache=NULL;
257    void *tmpptr;
258    static int auth_cache_size=0;
259    static int auth_cache_count=0;
260
261    if (auth_cache==NULL) {
262       DEBUGC(DBCLASS_AUTH,"initialize password cache");
263
264       /* config file not found or unable to open for read */
265       if (siproxd_passwordfile==NULL) {
266          ERROR ("could not open password file: %s", strerror(errno));
267          return NULL;
268       }
269       
270       rewind(siproxd_passwordfile);
271
272       while (fgets(buff,sizeof(buff),siproxd_passwordfile) != NULL) {
273          /* life insurance */
274          buff[sizeof(buff)-1]='\0';
275
276          /* strip newline if present */
277          if (buff[strlen(buff)-1]=='\n') buff[strlen(buff)-1]='\0';
278
279          /* strip emty lines */
280          if (strlen(buff) == 0) continue;
281
282          /* strip comments and line with only whitespaces */
283          for (i=0;i<strlen(buff);i++) {
284             if ((buff[i] == ' ') && (buff[i] == '\t')) continue;
285             if (buff[i] =='#') i=strlen(buff);
286             break;
287          }
288          if (i == strlen(buff)) continue;
289
290          /* allocate space whenever needed */
291          if (auth_cache_count >= auth_cache_size) {
292             auth_cache_size+=10;
293             tmpptr=realloc(auth_cache, auth_cache_size*sizeof(auth_cache_t));
294             if (tmpptr != NULL) {
295                auth_cache= (auth_cache_t *)tmpptr;
296             } else {
297                ERROR("realloc failed! this is not good");
298                auth_cache_size-=10;
299                return NULL;
300             }
301          } /* cnt > size */
302
303          i=sscanf(buff,"%s %s",auth_cache[auth_cache_count].username,
304                                auth_cache[auth_cache_count].password);
305          /* if I got username & passwd, make it valid and increment counter */
306          if (i == 2) auth_cache_count++;
307       }
308
309    } /* initialize cache */
310
311    /* search cache for user */
312    DEBUGC(DBCLASS_AUTH,"searching password entry for user %s",username);
313    for (i=0; i< auth_cache_count;i++) {
314       if (strcmp(username, auth_cache[i].username)==0) {
315          DEBUGC(DBCLASS_AUTH,"found password entry for user %s",username);
316          return auth_cache[i].password;
317       }
318    }
319
320    DEBUGC(DBCLASS_AUTH,"no password entry found for user %s",username);
321    return NULL;
322 }
323
324
325 /*-------------------------------------------------------------------------
326   -------------------------------------------------------------------------
327   The routines below have been taken from linphone
328   (osipua/src/authentication.c)
329   -------------------------------------------------------------------------
330   -------------------------------------------------------------------------*/
331
332 void CvtHex(
333             IN HASH Bin,
334             OUT HASHHEX Hex
335             )
336 {
337   unsigned short i;
338   unsigned char j;
339   
340   for (i = 0; i < HASHLEN; i++) {
341     j = (Bin[i] >> 4) & 0xf;
342     if (j <= 9)
343       Hex[i*2] = (j + '0');
344     else
345       Hex[i*2] = (j + 'a' - 10);
346     j = Bin[i] & 0xf;
347     if (j <= 9)
348       Hex[i*2+1] = (j + '0');
349     else
350       Hex[i*2+1] = (j + 'a' - 10);
351   };
352   Hex[HASHHEXLEN] = '\0';
353 }
354
355 /* calculate H(A1) as per spec */
356 void DigestCalcHA1(
357                    IN char * pszAlg,
358                    IN char * pszUserName,
359                    IN char * pszRealm,
360                    IN char * pszPassword,
361                    IN char * pszNonce,
362                    IN char * pszCNonce,
363                    OUT HASHHEX SessionKey
364                    )
365 {
366   MD5_CTX Md5Ctx;
367   HASH HA1;
368   
369   MD5Init(&Md5Ctx);
370   if (pszUserName) MD5Update(&Md5Ctx, pszUserName, strlen(pszUserName));
371   MD5Update(&Md5Ctx, ":", 1);
372   if (pszRealm)    MD5Update(&Md5Ctx, pszRealm, strlen(pszRealm));
373   MD5Update(&Md5Ctx, ":", 1);
374   if (pszPassword) MD5Update(&Md5Ctx, pszPassword, strlen(pszPassword));
375   MD5Final(HA1, &Md5Ctx);
376
377   if ((pszAlg!=NULL) && (osip_strcasecmp(pszAlg, "md5-sess") == 0)) {
378     MD5Init(&Md5Ctx);
379     MD5Update(&Md5Ctx, HA1, HASHLEN);
380     MD5Update(&Md5Ctx, ":", 1);
381     if (pszNonce)  MD5Update(&Md5Ctx, pszNonce, strlen(pszNonce));
382     MD5Update(&Md5Ctx, ":", 1);
383     if (pszCNonce) MD5Update(&Md5Ctx, pszCNonce, strlen(pszCNonce));
384     MD5Final(HA1, &Md5Ctx);
385   };
386   CvtHex(HA1, SessionKey);
387 }
388
389 /* calculate request-digest/response-digest as per HTTP Digest spec */
390 void DigestCalcResponse(
391                         IN HASHHEX HA1,         /* H(A1) */
392                         IN char * pszNonce,     /* nonce from server */
393                         IN char * pszNonceCount,  /* 8 hex digits */
394                         IN char * pszCNonce,    /* client nonce */
395                         IN char * pszQop,       /* qop-value: "", "auth", "auth-int" */
396                         IN char * pszMethod,    /* method from the request */
397                         IN char * pszDigestUri, /* requested URL */
398                         IN HASHHEX HEntity,     /* H(entity body) if qop="auth-int" */
399                         OUT HASHHEX Response    /* request-digest or response-digest */
400                         )
401 {
402   MD5_CTX Md5Ctx;
403   HASH HA2;
404   HASH RespHash;
405   HASHHEX HA2Hex;
406   
407   /* calculate H(A2) */
408   MD5Init(&Md5Ctx);
409   if (pszMethod)   MD5Update(&Md5Ctx, pszMethod, strlen(pszMethod));
410   MD5Update(&Md5Ctx, ":", 1);
411   if (pszDigestUri)MD5Update(&Md5Ctx, pszDigestUri, strlen(pszDigestUri));
412   
413   if (pszQop!=NULL) {
414       goto auth_withqop;
415   };
416   
417   /* auth_withoutqop: */
418   MD5Final(HA2, &Md5Ctx);
419   CvtHex(HA2, HA2Hex);
420
421   /* calculate response */
422   MD5Init(&Md5Ctx);
423   MD5Update(&Md5Ctx, HA1, HASHHEXLEN);
424   MD5Update(&Md5Ctx, ":", 1);
425   if (pszNonce)    MD5Update(&Md5Ctx, pszNonce, strlen(pszNonce));
426   MD5Update(&Md5Ctx, ":", 1);
427
428   goto end;
429
430  auth_withqop:
431
432   MD5Update(&Md5Ctx, ":", 1);
433   MD5Update(&Md5Ctx, HEntity, HASHHEXLEN);
434   MD5Final(HA2, &Md5Ctx);
435   CvtHex(HA2, HA2Hex);
436
437   /* calculate response */
438   MD5Init(&Md5Ctx);
439   MD5Update(&Md5Ctx, HA1, HASHHEXLEN);
440   MD5Update(&Md5Ctx, ":", 1);
441   if (pszNonce)    MD5Update(&Md5Ctx, pszNonce, strlen(pszNonce));
442   MD5Update(&Md5Ctx, ":", 1);
443   if (pszNonceCount)MD5Update(&Md5Ctx, pszNonceCount, strlen(pszNonceCount));
444   MD5Update(&Md5Ctx, ":", 1);
445   if (pszCNonce)   MD5Update(&Md5Ctx, pszCNonce, strlen(pszCNonce));
446   MD5Update(&Md5Ctx, ":", 1);
447   if (pszQop)      MD5Update(&Md5Ctx, pszQop, strlen(pszQop));
448   MD5Update(&Md5Ctx, ":", 1);
449
450  end:
451   MD5Update(&Md5Ctx, HA2Hex, HASHHEXLEN);
452   MD5Final(RespHash, &Md5Ctx);
453   CvtHex(RespHash, Response);
454 }
455
456