2 Copyright (C) 2002-2005 Thomas Ries <tries@gmx.net>
4 This file is part of Siproxd.
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.
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.
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
30 #include <netinet/in.h>
32 #include <osipparser2/osip_parser.h>
33 #include <osipparser2/osip_md5.h>
40 static char const ident[]="$Id: auth.c,v 1.18 2005/01/08 10:05:12 hb9xar Exp $";
42 /* configuration storage */
43 extern struct siproxd_config configuration;
45 /* Global File instance on pw file */
46 extern FILE *siproxd_passwordfile;
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);
54 * perform proxy authentication
57 * STS_SUCCESS : authentication ok / not needed
58 * STS_FAILURE : authentication failed
59 * STS_NEEDAUTH: authentication needed
61 int authenticate_proxy(sip_ticket_t *ticket) {
62 osip_proxy_authorization_t *proxy_auth=NULL;
64 /* required by config? */
65 if (configuration.proxy_auth_realm == NULL) {
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");
76 /* verify supplied authentication */
77 if (auth_check(proxy_auth) == STS_SUCCESS) {
78 DEBUGC(DBCLASS_AUTH,"proxy-auth succeeded");
82 /* authentication failed */
83 WARN("authenticate_proxy failed");
88 * includes proxy authentication header in SIP message
94 int auth_include_authrq(sip_ticket_t *ticket) {
95 osip_proxy_authenticate_t *p_auth;
98 if (osip_proxy_authenticate_init(&p_auth) != 0) {
99 ERROR("proxy_authenticate_init failed");
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 */
107 sprintf(realm,"\"%s\"",configuration.proxy_auth_realm);
108 osip_proxy_authenticate_set_realm(p_auth, realm);
110 ERROR("unable to malloc() %i bytes for authentication realm",
111 strlen(configuration.proxy_auth_realm)+3);
115 osip_list_add (ticket->sipmsg->proxy_authenticates, p_auth, -1);
117 DEBUGC(DBCLASS_AUTH,"added authentication header");
123 * generates a nonce string
125 * RETURNS nonce string
127 static char *auth_generate_nonce() {
128 static char nonce[40];
131 gettimeofday (&tv, NULL);
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() );
138 DEBUGC(DBCLASS_AUTH,"created nonce=\"%s\"",nonce);
144 * verify the supplied authentication information from UA
147 * STS_SUCCESS if succeeded
148 * STS_FAILURE if failed
150 static int auth_check(osip_proxy_authorization_t *proxy_auth) {
156 HASHHEX Lcl_Response;
158 char *Username = NULL;
162 char *NonceCount = NULL;
165 char *Response = NULL;
167 /* if item exists, allocate& copy string without quotes */
168 if (proxy_auth->username)
169 Username=osip_strdup_without_quote(proxy_auth->username);
171 if (proxy_auth->realm)
172 Realm=osip_strdup_without_quote(proxy_auth->realm);
174 if (proxy_auth->nonce)
175 Nonce=osip_strdup_without_quote(proxy_auth->nonce);
177 if (proxy_auth->cnonce)
178 CNonce=osip_strdup_without_quote(proxy_auth->cnonce);
180 if (proxy_auth->nonce_count)
181 NonceCount=osip_strdup_without_quote(proxy_auth->nonce_count);
183 if (proxy_auth->message_qop)
184 Qpop=osip_strdup_without_quote(proxy_auth->message_qop);
187 Uri=osip_strdup_without_quote(proxy_auth->uri);
189 if (proxy_auth->response)
190 Response=osip_strdup_without_quote(proxy_auth->response);
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;
201 if (password == NULL) password="";
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 );
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);
217 DEBUGC(DBCLASS_BABBLE,"calculated Response=\"%s\"", Lcl_Response);
219 if (strcmp(Lcl_Response, Response)==0) {
220 DEBUGC(DBCLASS_AUTH,"Authentication succeeded");
223 DEBUGC(DBCLASS_AUTH,"Authentication failed");
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);
235 if (Response) free(Response);
242 * lookup in the password file and return
243 * the user specific password for 'username'
246 * password for user or NULL if not found
248 static char *auth_getpwd(char *username) {
250 char username[USERNAME_SIZE];
251 char password[PASSWORD_SIZE];
256 static auth_cache_t *auth_cache=NULL;
258 static int auth_cache_size=0;
259 static int auth_cache_count=0;
261 if (auth_cache==NULL) {
262 DEBUGC(DBCLASS_AUTH,"initialize password cache");
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));
270 rewind(siproxd_passwordfile);
272 while (fgets(buff,sizeof(buff),siproxd_passwordfile) != NULL) {
274 buff[sizeof(buff)-1]='\0';
276 /* strip newline if present */
277 if (buff[strlen(buff)-1]=='\n') buff[strlen(buff)-1]='\0';
279 /* strip emty lines */
280 if (strlen(buff) == 0) continue;
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);
288 if (i == strlen(buff)) continue;
290 /* allocate space whenever needed */
291 if (auth_cache_count >= auth_cache_size) {
293 tmpptr=realloc(auth_cache, auth_cache_size*sizeof(auth_cache_t));
294 if (tmpptr != NULL) {
295 auth_cache= (auth_cache_t *)tmpptr;
297 ERROR("realloc failed! this is not good");
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++;
309 } /* initialize cache */
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;
320 DEBUGC(DBCLASS_AUTH,"no password entry found for user %s",username);
325 /*-------------------------------------------------------------------------
326 -------------------------------------------------------------------------
327 The routines below have been taken from linphone
328 (osipua/src/authentication.c)
329 -------------------------------------------------------------------------
330 -------------------------------------------------------------------------*/
340 for (i = 0; i < HASHLEN; i++) {
341 j = (Bin[i] >> 4) & 0xf;
343 Hex[i*2] = (j + '0');
345 Hex[i*2] = (j + 'a' - 10);
348 Hex[i*2+1] = (j + '0');
350 Hex[i*2+1] = (j + 'a' - 10);
352 Hex[HASHHEXLEN] = '\0';
355 /* calculate H(A1) as per spec */
358 IN char * pszUserName,
360 IN char * pszPassword,
363 OUT HASHHEX SessionKey
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);
377 if ((pszAlg!=NULL) && (osip_strcasecmp(pszAlg, "md5-sess") == 0)) {
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);
386 CvtHex(HA1, SessionKey);
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 */
407 /* calculate H(A2) */
409 if (pszMethod) MD5Update(&Md5Ctx, pszMethod, strlen(pszMethod));
410 MD5Update(&Md5Ctx, ":", 1);
411 if (pszDigestUri)MD5Update(&Md5Ctx, pszDigestUri, strlen(pszDigestUri));
417 /* auth_withoutqop: */
418 MD5Final(HA2, &Md5Ctx);
421 /* calculate response */
423 MD5Update(&Md5Ctx, HA1, HASHHEXLEN);
424 MD5Update(&Md5Ctx, ":", 1);
425 if (pszNonce) MD5Update(&Md5Ctx, pszNonce, strlen(pszNonce));
426 MD5Update(&Md5Ctx, ":", 1);
432 MD5Update(&Md5Ctx, ":", 1);
433 MD5Update(&Md5Ctx, HEntity, HASHHEXLEN);
434 MD5Final(HA2, &Md5Ctx);
437 /* calculate response */
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);
451 MD5Update(&Md5Ctx, HA2Hex, HASHHEXLEN);
452 MD5Final(RespHash, &Md5Ctx);
453 CvtHex(RespHash, Response);