1 /*
2 * Digest Authentication - Database support
3 *
4 * Copyright (C) 2001-2003 FhG Fokus
5 *
6 * This file is part of ser, a free SIP server.
7 *
8 * ser is free software; you can redistribute it and/or modify
9 * it under the terms of the GNU General Public License as published by
10 * the Free Software Foundation; either version 2 of the License, or
11 * (at your option) any later version
12 *
13 * For a license to use the ser software under conditions
14 * other than those described here, or to purchase support for this
15 * software, please contact iptel.org by e-mail at the following addresses:
16 * info@iptel.org
17 *
18 * ser is distributed in the hope that it will be useful,
19 * but WITHOUT ANY WARRANTY; without even the implied warranty of
20 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
21 * GNU General Public License for more details.
22 *
23 * You should have received a copy of the GNU General Public License
24 * along with this program; if not, write to the Free Software
25 * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA
26 *
27 */
28
29
30 #include <stdio.h>
31 #include <stdlib.h>
32 #include <string.h>
33 #include "../../core/ut.h"
34 #include "../../core/str.h"
35 #include "../../lib/srdb2/db.h"
36 #include "../../core/dprint.h"
37 #include "../../core/parser/digest/digest.h"
38 #include "../../core/parser/hf.h"
39 #include "../../core/parser/parser_f.h"
40 #include "../../core/usr_avp.h"
41 #include "../../core/mem/mem.h"
42 #include "../../core/config.h"
43 #include "../../core/id.h"
44 #include "../../core/sr_module.h"
45 #include "../../modules/auth/api.h"
46 #include "uid_auth_db_mod.h"
47
48
49 #define IS_NULL(f) ((f).flags & DB_NULL)
50
get_ha1(struct username * username,str * did,str * realm,authdb_table_info_t * table_info,char * ha1,db_res_t ** res,db_rec_t ** row)51 static inline int get_ha1(struct username* username, str* did, str* realm,
52 authdb_table_info_t *table_info, char* ha1, db_res_t** res, db_rec_t** row)
53 {
54 str result;
55 db_cmd_t *q = NULL;
56
57 if (calc_ha1) {
58 q = table_info->query_password;
59 LM_DBG("querying plain password\n");
60 } else {
61 if (username->domain.len) {
62 q = table_info->query_pass2;
63 LM_DBG("querying ha1b\n");
64 } else {
65 q = table_info->query_pass;
66 LM_DBG("querying ha1\n");
67 }
68 }
69
70 q->match[0].v.lstr = username->user;
71 q->match[1].v.lstr = *realm;
72
73 if (use_did) q->match[2].v.lstr = *did;
74
75 if (db_exec(res, q) < 0 ) {
76 LM_ERR("Error while querying database\n");
77 return -1;
78 }
79
80 if (*res) *row = db_first(*res);
81 else *row = NULL;
82 while (*row) {
83 if (IS_NULL((*row)->fld[0]) || IS_NULL((*row)->fld[1])) {
84 LM_ERR("Credentials for '%.*s'@'%.*s' contain NULL value,"
85 " skipping\n",
86 username->user.len, ZSW(username->user.s),
87 realm->len, ZSW(realm->s));
88 } else {
89 if ((*row)->fld[1].v.int4 & SRDB_DISABLED) {
90 /* disabled rows ignored */
91 } else {
92 if ((*row)->fld[1].v.int4 & SRDB_LOAD_SER) {
93 /* *row = i; */
94 break;
95 }
96 }
97 }
98 *row = db_next(*res);
99 }
100
101 if (!*row) {
102 LM_DBG("Credentials for '%.*s'@'%.*s' not found\n",
103 username->user.len, ZSW(username->user.s),
104 realm->len, ZSW(realm->s));
105 return 1;
106 }
107
108 result.s = (*row)->fld[0].v.cstr;
109 result.len = strlen(result.s);
110
111 if (calc_ha1) {
112 /* Only plaintext passwords are stored in database,
113 * we have to calculate HA1 */
114 auth_api.calc_HA1(HA_MD5, &username->whole, realm, &result, 0, 0, ha1);
115 LM_DBG("HA1 string calculated: %s\n", ha1);
116 } else {
117 memcpy(ha1, result.s, result.len);
118 ha1[result.len] = '\0';
119 }
120
121 return 0;
122 }
123
124 /*
125 * Calculate the response and compare with the given response string
126 * Authorization is successful if this two strings are same
127 */
check_response(dig_cred_t * cred,str * method,char * ha1)128 static inline int check_response(dig_cred_t* cred, str* method, char* ha1)
129 {
130 HASHHEX resp, hent;
131
132 /*
133 * First, we have to verify that the response received has
134 * the same length as responses created by us
135 */
136 if (cred->response.len != 32) {
137 LM_DBG("Receive response len != 32\n");
138 return 1;
139 }
140
141 /*
142 * Now, calculate our response from parameters received
143 * from the user agent
144 */
145 auth_api.calc_response(ha1, &(cred->nonce),
146 &(cred->nc), &(cred->cnonce),
147 &(cred->qop.qop_str), cred->qop.qop_parsed == QOP_AUTHINT,
148 method, &(cred->uri), hent, resp);
149
150 LM_DBG("Our result = \'%s\'\n", resp);
151
152 /*
153 * And simply compare the strings, the user is
154 * authorized if they match
155 */
156 if (!memcmp(resp, cred->response.s, 32)) {
157 LM_DBG("Authorization is OK\n");
158 return 0;
159 } else {
160 LM_DBG("Authorization failed\n");
161 return 2;
162 }
163 }
164
165
166 /*
167 * Generate AVPs from the database result
168 */
generate_avps(db_res_t * result,db_rec_t * row)169 static int generate_avps(db_res_t* result, db_rec_t *row)
170 {
171 int i;
172 int_str iname, ivalue;
173 str value;
174 char buf[32];
175
176 for (i = 2; i < credentials_n + 2; i++) {
177 value = row->fld[i].v.lstr;
178
179 if (IS_NULL(row->fld[i]))
180 continue;
181
182 switch (row->fld[i].type) {
183 case DB_STR:
184 value = row->fld[i].v.lstr;
185 break;
186
187 case DB_INT:
188 value.len = sprintf(buf, "%d", row->fld[i].v.int4);
189 value.s = buf;
190 break;
191
192 default:
193 abort();
194 break;
195 }
196
197 if (value.s == NULL)
198 continue;
199
200 iname.s = credentials[i - 2];
201 ivalue.s = value;
202
203 if (add_avp(AVP_NAME_STR | AVP_VAL_STR | AVP_CLASS_USER,
204 iname, ivalue) < 0) {
205 LM_ERR("Error while creating AVPs\n");
206 return -1;
207 }
208
209 LM_DBG("set string AVP \'%.*s = %.*s\'\n",
210 iname.s.len, ZSW(iname.s.s), value.len, ZSW(value.s));
211 }
212
213 return 0;
214 }
215
216 /* this is a dirty work around to check the credentials of all users,
217 * if the database query returned more then one result
218 *
219 * Fills res (which must be db_free'd afterwards if the call was succesfull)
220 * returns 0 on success, 1 on no match (?)
221 * and -1 on error (memory, db a.s.o).
222 * WARNING: if -1 is returned res _must_ _not_ be freed (it's empty)
223 *
224 */
check_all_ha1(struct sip_msg * msg,struct hdr_field * hdr,dig_cred_t * dig,str * method,str * did,str * realm,authdb_table_info_t * table_info,db_res_t ** res)225 static inline int check_all_ha1(struct sip_msg* msg, struct hdr_field* hdr,
226 dig_cred_t* dig, str* method, str* did, str* realm,
227 authdb_table_info_t *table_info, db_res_t** res)
228 {
229 char ha1[256];
230 db_rec_t *row;
231 str result;
232 db_cmd_t *q;
233
234 if (calc_ha1) {
235 q = table_info->query_password;
236 LM_DBG("querying plain password\n");
237 }
238 else {
239 if (dig->username.domain.len) {
240 q = table_info->query_pass2;
241 LM_DBG("querying ha1b\n");
242 }
243 else {
244 q = table_info->query_pass;
245 LM_DBG("querying ha1\n");
246 }
247 }
248
249 q->match[0].v.lstr = dig->username.user;
250 if (dig->username.domain.len)
251 q->match[1].v.lstr = dig->username.domain;
252 else
253 q->match[1].v.lstr = *realm;
254
255 if (use_did) q->match[2].v.lstr = *did;
256
257 if (db_exec(res, q) < 0 ) {
258 LM_ERR("Error while querying database\n");
259 }
260
261 if (*res) row = db_first(*res);
262 else row = NULL;
263 while (row) {
264 if (IS_NULL(row->fld[0]) || IS_NULL(row->fld[1])) {
265 LM_ERR("Credentials for '%.*s'@'%.*s' contain NULL value,"
266 " skipping\n",
267 dig->username.user.len, ZSW(dig->username.user.s),
268 realm->len, ZSW(realm->s));
269 }
270 else {
271 if (row->fld[1].v.int4 & SRDB_DISABLED) {
272 /* disabled rows ignored */
273 }
274 else {
275 if (row->fld[1].v.int4 & SRDB_LOAD_SER) {
276 result.s = row->fld[0].v.cstr;
277 result.len = strlen(result.s);
278 if (calc_ha1) {
279 /* Only plaintext passwords are stored in database,
280 * we have to calculate HA1 */
281 auth_api.calc_HA1(HA_MD5, &(dig->username.whole),
282 realm, &result, 0, 0, ha1);
283 LM_DBG("HA1 string calculated: %s\n", ha1);
284 } else {
285 memcpy(ha1, result.s, result.len);
286 ha1[result.len] = '\0';
287 }
288
289 if (!check_response(dig, method, ha1)) {
290 if (auth_api.post_auth(msg, hdr, ha1)
291 == AUTHENTICATED) {
292 generate_avps(*res, row);
293 return 0;
294 }
295 }
296 }
297 }
298 }
299 row = db_next(*res);
300 }
301
302 if (!row) {
303 LM_DBG("Credentials for '%.*s'@'%.*s' not found",
304 dig->username.user.len, ZSW(dig->username.user.s),
305 realm->len, ZSW(realm->s));
306 }
307 return 1;
308
309
310 }
311
312
313 /*
314 * Authenticate digest credentials
315 * Returns:
316 * -3 -- Bad Request
317 * -2 -- Error while checking credentials (such as malformed message or database problem)
318 * -1 -- Authentication failed
319 * 1 -- Authentication successful
320 */
authenticate(struct sip_msg * msg,str * realm,authdb_table_info_t * table,hdr_types_t hftype)321 static inline int authenticate(struct sip_msg* msg, str* realm,
322 authdb_table_info_t *table, hdr_types_t hftype)
323 {
324 char ha1[256];
325 int res, ret;
326 db_rec_t *row;
327 struct hdr_field* h;
328 auth_body_t* cred;
329 db_res_t* result;
330 str did;
331
332 cred = 0;
333 result = 0;
334 ret = -1;
335
336 switch(auth_api.pre_auth(msg, realm, hftype, &h, NULL)) {
337 case NONCE_REUSED:
338 LM_DBG("nonce reused");
339 ret = AUTH_NONCE_REUSED;
340 goto end;
341 case STALE_NONCE:
342 LM_DBG("stale nonce\n");
343 ret = AUTH_STALE_NONCE;
344 goto end;
345 case NO_CREDENTIALS:
346 LM_DBG("no credentials\n");
347 ret = AUTH_NO_CREDENTIALS;
348 goto end;
349 case ERROR:
350 case BAD_CREDENTIALS:
351 ret = -3;
352 goto end;
353 case CREATE_CHALLENGE:
354 LM_ERR("CREATE_CHALLENGE is not a valid state\n");
355 ret = -2;
356 goto end;
357 case DO_RESYNCHRONIZATION:
358 LM_ERR("DO_RESYNCHRONIZATION is not a valid state\n");
359 ret = -2;
360 goto end;
361
362 case NOT_AUTHENTICATED:
363 ret = -1;
364 goto end;
365
366 case DO_AUTHENTICATION:
367 break;
368
369 case AUTHENTICATED:
370 ret = 1;
371 goto end;
372 }
373
374 cred = (auth_body_t*)h->parsed;
375
376 if (use_did) {
377 if (msg->REQ_METHOD == METHOD_REGISTER) {
378 ret = get_to_did(&did, msg);
379 } else {
380 ret = get_from_did(&did, msg);
381 }
382 if (ret == 0) {
383 did.s = DEFAULT_DID;
384 did.len = sizeof(DEFAULT_DID) - 1;
385 }
386 } else {
387 did.len = 0;
388 did.s = 0;
389 }
390
391
392 if (check_all) {
393 res = check_all_ha1(msg, h, &(cred->digest),
394 &msg->first_line.u.request.method, &did, realm, table, &result);
395 if (res < 0) {
396 ret = -2;
397 goto end;
398 }
399 else if (res > 0) {
400 ret = -1;
401 goto end;
402 }
403 else {
404 ret = 1;
405 goto end;
406 }
407 } else {
408 res = get_ha1(&cred->digest.username, &did, realm, table, ha1,
409 &result, &row);
410 if (res < 0) {
411 ret = -2;
412 goto end;
413 }
414 if (res > 0) {
415 /* Username not found in the database */
416 ret = -1;
417 goto end;
418 }
419 }
420
421 /* Recalculate response, it must be same to authorize successfully */
422 if (!check_response(&(cred->digest), &msg->first_line.u.request.method,
423 ha1)) {
424 switch(auth_api.post_auth(msg, h, ha1)) {
425 case ERROR:
426 case BAD_CREDENTIALS:
427 ret = -2;
428 break;
429
430 case NOT_AUTHENTICATED:
431 ret = -1;
432 break;
433
434 case AUTHENTICATED:
435 generate_avps(result, row);
436 ret = 1;
437 break;
438
439 default:
440 ret = -1;
441 break;
442 }
443 } else {
444 ret = -1;
445 }
446
447 end:
448 if (result) db_res_free(result);
449 if (ret < 0) {
450 if (auth_api.build_challenge(msg, (cred ? cred->stale : 0), realm,
451 NULL, NULL, hftype) < 0) {
452 LM_ERR("Error while creating challenge\n");
453 ret = -2;
454 }
455 }
456 return ret;
457 }
458
459
460 /*
461 * Authenticate using Proxy-Authorize header field
462 */
proxy_authenticate(struct sip_msg * msg,char * p1,char * p2)463 int proxy_authenticate(struct sip_msg* msg, char* p1, char* p2)
464 {
465 str realm;
466
467 if (get_str_fparam(&realm, msg, (fparam_t*)p1) < 0) {
468 LM_ERR("Cannot obtain digest realm from parameter '%s'\n",
469 ((fparam_t*)p1)->orig);
470 return -1;
471 }
472
473 return authenticate(msg, &realm, (authdb_table_info_t*)p2, HDR_PROXYAUTH_T);
474 }
475
476
477 /*
478 * Authorize using WWW-Authorize header field
479 */
www_authenticate(struct sip_msg * msg,char * p1,char * p2)480 int www_authenticate(struct sip_msg* msg, char* p1, char* p2)
481 {
482 str realm;
483
484 if (get_str_fparam(&realm, msg, (fparam_t*)p1) < 0) {
485 LM_ERR("Cannot obtain digest realm from parameter '%s'\n",
486 ((fparam_t*)p1)->orig);
487 return -1;
488 }
489
490 return authenticate(msg, &realm, (authdb_table_info_t*)p2,
491 HDR_AUTHORIZATION_T);
492 }
493