![]() |
Home page |
Mailing list |
Docs
Asterisk developer's documentation :: Codename Pineapple
sip3_auth.c File Reference
Olle E. Johansson <oej@edvina.net> (all the chan_sip3 changes)
Definition in file sip3_auth.c.
#include "asterisk.h"
#include <stdio.h>
#include <ctype.h>
#include <string.h>
#include <unistd.h>
#include <sys/socket.h>
#include <sys/ioctl.h>
#include <net/if.h>
#include <errno.h>
#include <stdlib.h>
#include <fcntl.h>
#include <netdb.h>
#include <signal.h>
#include <sys/signal.h>
#include <netinet/in.h>
#include <netinet/in_systm.h>
#include <arpa/inet.h>
#include <netinet/ip.h>
#include <regex.h>
#include "asterisk/lock.h"
#include "asterisk/channel.h"
#include "asterisk/config.h"
#include "asterisk/logger.h"
#include "asterisk/module.h"
#include "asterisk/pbx.h"
#include "asterisk/options.h"
#include "asterisk/sched.h"
#include "asterisk/io.h"
#include "asterisk/rtp.h"
#include "asterisk/udptl.h"
#include "asterisk/acl.h"
#include "asterisk/manager.h"
#include "asterisk/callerid.h"
#include "asterisk/cli.h"
#include "asterisk/app.h"
#include "asterisk/musiconhold.h"
#include "asterisk/dsp.h"
#include "asterisk/features.h"
#include "asterisk/srv.h"
#include "asterisk/astdb.h"
#include "asterisk/causes.h"
#include "asterisk/utils.h"
#include "asterisk/file.h"
#include "asterisk/astobj.h"
#include "asterisk/dnsmgr.h"
#include "asterisk/devicestate.h"
#include "asterisk/linkedlists.h"
#include "asterisk/stringfields.h"
#include "asterisk/monitor.h"
#include "asterisk/localtime.h"
#include "asterisk/abstract_jb.h"
#include "asterisk/compiler.h"
#include "sip3.h"
#include "sip3funcs.h"
Include dependency graph for sip3_auth.c:

Go to the source code of this file.
Functions | |
| sip_auth * | add_realm_authentication (struct sip_auth *authlist, char *configuration, int lineno) |
| Add realm authentication in list. | |
| void | auth_headers (enum sip_auth_type code, char **header, char **respheader) |
| return the request and response heade for a 401 or 407 code | |
| int | build_reply_digest (struct sip_dialog *p, int method, char *digest, int digest_len) |
| Build reply digest. | |
| enum check_auth_result | check_auth (struct sip_dialog *p, struct sip_request *req, const char *username, const char *secret, const char *md5secret, int sipmethod, char *uri, enum xmittype reliable, int ignore) |
| Check user authorization from peer definition Some actions, like REGISTER and INVITEs from peers require authentication (if peer have secret set). | |
| int | clear_realm_authentication (struct sip_auth *authlist) |
| Clear realm authentication list (at reload). | |
| int | do_proxy_auth (struct sip_dialog *p, struct sip_request *req, enum sip_auth_type code, int sipmethod, int init) |
| Add authentication on outbound SIP packet. | |
| int | do_register_auth (struct sip_dialog *p, struct sip_request *req, enum sip_auth_type code) |
| Authenticate for outbound registration. | |
| sip_auth * | find_realm_authentication (struct sip_auth *authlist, const char *realm) |
| Find authentication for a specific realm. | |
| int | reply_digest (struct sip_dialog *p, struct sip_request *req, char *header, int sipmethod, char *digest, int digest_len) |
| reply to authentication for outbound registrations | |
Variables | |
| sip_auth * | authl = NULL |
|
||||||||||||||||
|
Add realm authentication in list.
Definition at line 495 of file sip3_auth.c. References ast_calloc, ast_log(), ast_strlen_zero(), ast_verbose(), LOG_DEBUG, LOG_WARNING, sip_auth::md5secret, sip_auth::next, option_debug, option_verbose, sip_auth::realm, secret, strsep(), and sip_auth::username. 00496 { 00497 char authcopy[256]; 00498 char *username=NULL, *realm=NULL, *secret=NULL, *md5secret=NULL; 00499 char *stringp; 00500 struct sip_auth *a, *b, *auth; 00501 00502 if (ast_strlen_zero(configuration)) 00503 return authlist; 00504 00505 if (option_debug) 00506 ast_log(LOG_DEBUG, "Auth config :: %s\n", configuration); 00507 00508 ast_copy_string(authcopy, configuration, sizeof(authcopy)); 00509 stringp = authcopy; 00510 00511 username = stringp; 00512 realm = strrchr(stringp, '@'); 00513 if (realm) 00514 *realm++ = '\0'; 00515 if (ast_strlen_zero(username) || ast_strlen_zero(realm)) { 00516 ast_log(LOG_WARNING, "Format for authentication entry is user[:secret]@realm at line %d\n", lineno); 00517 return authlist; 00518 } 00519 stringp = username; 00520 username = strsep(&stringp, ":"); 00521 if (username) { 00522 secret = strsep(&stringp, ":"); 00523 if (!secret) { 00524 stringp = username; 00525 md5secret = strsep(&stringp,"#"); 00526 } 00527 } 00528 if (!(auth = ast_calloc(1, sizeof(*auth)))) 00529 return authlist; 00530 00531 ast_copy_string(auth->realm, realm, sizeof(auth->realm)); 00532 ast_copy_string(auth->username, username, sizeof(auth->username)); 00533 if (secret) 00534 ast_copy_string(auth->secret, secret, sizeof(auth->secret)); 00535 if (md5secret) 00536 ast_copy_string(auth->md5secret, md5secret, sizeof(auth->md5secret)); 00537 00538 /* find the end of the list */ 00539 for (b = NULL, a = authlist; a ; b = a, a = a->next) 00540 ; 00541 if (b) 00542 b->next = auth; /* Add structure add end of list */ 00543 else 00544 authlist = auth; 00545 00546 if (option_verbose > 2) 00547 ast_verbose("Added authentication for realm %s\n", realm); 00548 00549 return authlist; 00550 00551 }
|
|
||||||||||||||||
|
return the request and response heade for a 401 or 407 code Definition at line 133 of file sip3_auth.c. References ast_verbose(), PROXY_AUTH, and WWW_AUTH. 00134 { 00135 if (code == WWW_AUTH) { /* 401 */ 00136 *header = "WWW-Authenticate"; 00137 *respheader = "Authorization"; 00138 } else if (code == PROXY_AUTH) { /* 407 */ 00139 *header = "Proxy-Authenticate"; 00140 *respheader = "Proxy-Authorization"; 00141 } else { 00142 ast_verbose("-- wrong response code %d\n", code); 00143 *header = *respheader = "Invalid"; 00144 } 00145 }
|
|
||||||||||||||||||||
|
Build reply digest.
Definition at line 428 of file sip3_auth.c. References ast_inet_ntoa(), ast_log(), ast_md5_hash(), ast_random(), ast_strlen_zero(), authl, find_realm_authentication(), LOG_DEBUG, LOG_WARNING, sip_auth::md5secret, sip_dialog::noncecount, sip_dialog::sa, secret, sip_auth::secret, sip_method2txt(), and sip_auth::username. 00429 { 00430 char a1[256]; 00431 char a2[256]; 00432 char a1_hash[256]; 00433 char a2_hash[256]; 00434 char resp[256]; 00435 char resp_hash[256]; 00436 char uri[256]; 00437 char cnonce[80]; 00438 const char *username; 00439 const char *secret; 00440 const char *md5secret; 00441 struct sip_auth *auth = NULL; /* Realm authentication */ 00442 00443 if (!ast_strlen_zero(p->domain)) 00444 ast_copy_string(uri, p->domain, sizeof(uri)); 00445 else if (!ast_strlen_zero(p->uri)) 00446 ast_copy_string(uri, p->uri, sizeof(uri)); 00447 else 00448 snprintf(uri, sizeof(uri), "sip:%s@%s",p->peername, ast_inet_ntoa(p->sa.sin_addr)); 00449 00450 snprintf(cnonce, sizeof(cnonce), "%08lx", ast_random()); 00451 00452 /* Check if we have separate auth credentials */ 00453 if ((auth = find_realm_authentication(authl, p->realm))) { 00454 ast_log(LOG_WARNING, "use realm [%s] from peer [%s]\n", 00455 auth->username, p->peername); 00456 username = auth->username; 00457 secret = auth->secret; 00458 md5secret = auth->md5secret; 00459 if (sipdebug) 00460 ast_log(LOG_DEBUG,"Using realm %s authentication for call %s\n", p->realm, p->callid); 00461 } else { 00462 /* No authentication, use peer or register= config */ 00463 username = p->authname; 00464 secret = p->peersecret; 00465 md5secret = p->peermd5secret; 00466 } 00467 if (ast_strlen_zero(username)) /* We have no authentication */ 00468 return -1; 00469 00470 /* Calculate SIP digest response */ 00471 snprintf(a1,sizeof(a1), "%s:%s:%s", username, p->realm, secret); 00472 snprintf(a2,sizeof(a2), "%s:%s", sip_method2txt(method), uri); 00473 if (!ast_strlen_zero(md5secret)) 00474 ast_copy_string(a1_hash, md5secret, sizeof(a1_hash)); 00475 else 00476 ast_md5_hash(a1_hash, a1); 00477 ast_md5_hash(a2_hash,a2); 00478 00479 p->noncecount++; 00480 if (!ast_strlen_zero(p->qop)) 00481 snprintf(resp,sizeof(resp),"%s:%s:%08x:%s:%s:%s", a1_hash, p->nonce, p->noncecount, cnonce, "auth", a2_hash); 00482 else 00483 snprintf(resp,sizeof(resp),"%s:%s:%s", a1_hash, p->nonce, a2_hash); 00484 ast_md5_hash(resp_hash, resp); 00485 /* XXX We hard code our qop to "auth" for now. XXX */ 00486 if (!ast_strlen_zero(p->qop)) 00487 snprintf(digest, digest_len, "Digest username=\"%s\", realm=\"%s\", algorithm=MD5, uri=\"%s\", nonce=\"%s\", response=\"%s\", opaque=\"%s\", qop=auth, cnonce=\"%s\", nc=%08x", username, p->realm, uri, p->nonce, resp_hash, p->opaque, cnonce, p->noncecount); 00488 else 00489 snprintf(digest, digest_len, "Digest username=\"%s\", realm=\"%s\", algorithm=MD5, uri=\"%s\", nonce=\"%s\", response=\"%s\", opaque=\"%s\"", username, p->realm, uri, p->nonce, resp_hash, p->opaque); 00490 00491 return 0; 00492 }
|
|
||||||||||||||||||||||||||||||||||||||||
|
Check user authorization from peer definition Some actions, like REGISTER and INVITEs from peers require authentication (if peer have secret set).
Definition at line 152 of file sip3_auth.c. References ast_random(), ast_string_field_build, ast_strlen_zero(), AUTH_CHALLENGE_SENT, auth_headers(), AUTH_SUCCESSFUL, DEFAULT_TRANS_TIMEOUT, FALSE, get_header(), key(), keys, s, sip_scheddestroy(), strsep(), transmit_response_with_auth(), and WWW_AUTH. 00155 { 00156 const char *response = "407 Proxy Authentication Required"; 00157 char *reqheader; 00158 char *respheader; 00159 const char *authtoken; 00160 char a1_hash[256]; 00161 char resp_hash[256]=""; 00162 char tmp[BUFSIZ * 2]; /* Make a large enough buffer */ 00163 char *c; 00164 int wrongnonce = FALSE; 00165 int good_response; 00166 const char *usednonce = p->randdata; 00167 00168 /* table of recognised keywords, and their value in the digest */ 00169 enum keys { K_RESP, K_URI, K_USER, K_NONCE, K_LAST }; 00170 struct x { 00171 const char *key; 00172 const char *s; 00173 } *i, keys[] = { 00174 [K_RESP] = { "response=", "" }, 00175 [K_URI] = { "uri=", "" }, 00176 [K_USER] = { "username=", "" }, 00177 [K_NONCE] = { "nonce=", "" }, 00178 [K_LAST] = { NULL, NULL} 00179 }; 00180 00181 /* Always OK if no secret */ 00182 if (ast_strlen_zero(secret) && ast_strlen_zero(md5secret)) 00183 return AUTH_SUCCESSFUL; 00184 response = "401 Unauthorized"; 00185 auth_headers(WWW_AUTH, &respheader, &reqheader); 00186 authtoken = get_header(req, reqheader); 00187 if (ignore && !ast_strlen_zero(p->randdata) && ast_strlen_zero(authtoken)) { 00188 /* This is a retransmitted invite/register/etc, don't reconstruct authentication 00189 information */ 00190 if (!reliable) { 00191 /* Resend message if this was NOT a reliable delivery. Otherwise the 00192 retransmission should get it */ 00193 transmit_response_with_auth(p, response, req, p->randdata, reliable, respheader, 0); 00194 /* Schedule auto destroy in 32 seconds (according to RFC 3261) */ 00195 sip_scheddestroy(p, DEFAULT_TRANS_TIMEOUT); 00196 } 00197 return AUTH_CHALLENGE_SENT; 00198 } else if (ast_strlen_zero(p->randdata) || ast_strlen_zero(authtoken)) { 00199 /* We have no auth, so issue challenge and request authentication */ 00200 ast_string_field_build(p, randdata, "%08lx", ast_random()); /* Create nonce for challenge */ 00201 transmit_response_with_auth(p, response, req, p->randdata, reliable, respheader, 0); 00202 /* Schedule auto destroy in 32 seconds */ 00203 sip_scheddestroy(p, DEFAULT_TRANS_TIMEOUT); 00204 return AUTH_CHALLENGE_SENT; 00205 } 00206 00207 /* --- We have auth, so check it */ 00208 00209 /* Whoever came up with the authentication section of SIP can suck my %&#$&* for not putting 00210 an example in the spec of just what it is you're doing a hash on. */ 00211 00212 00213 /* Make a copy of the response and parse it */ 00214 ast_copy_string(tmp, authtoken, sizeof(tmp)); 00215 c = tmp; 00216 00217 while(c && *(c = ast_skip_blanks(c)) ) { /* lookup for keys */ 00218 for (i = keys; i->key != NULL; i++) { 00219 const char *separator = ","; /* default */ 00220 00221 if (strncasecmp(c, i->key, strlen(i->key)) != 0) 00222 continue; 00223 /* Found. Skip keyword, take text in quotes or up to the separator. */ 00224 c += strlen(i->key); 00225 if (*c == '"') { /* in quotes. Skip first and look for last */ 00226 c++; 00227 separator = "\""; 00228 } 00229 i->s = c; 00230 strsep(&c, separator); 00231 break; 00232 } 00233 if (i->key == NULL) /* not found, jump after space or comma */ 00234 strsep(&c, " ,"); 00235 } 00236 00237 /* Verify that digest username matches the username we auth as */ 00238 if (strcmp(username, keys[K_USER].s)) { 00239 ast_log(LOG_WARNING, "username mismatch, have <%s>, digest has <%s>\n", 00240 username, keys[K_USER].s); 00241 /* Oops, we're trying something here */ 00242 return AUTH_USERNAME_MISMATCH; 00243 } 00244 00245 /* Verify nonce from request matches our nonce. If not, send 401 with new nonce */ 00246 if (strcasecmp(p->randdata, keys[K_NONCE].s)) { /* XXX it was 'n'casecmp ? */ 00247 wrongnonce = TRUE; 00248 usednonce = keys[K_NONCE].s; 00249 } 00250 00251 if (!ast_strlen_zero(md5secret)) 00252 ast_copy_string(a1_hash, md5secret, sizeof(a1_hash)); 00253 else { 00254 char a1[256]; 00255 snprintf(a1, sizeof(a1), "%s:%s:%s", username, global.realm, secret); 00256 ast_md5_hash(a1_hash, a1); 00257 } 00258 00259 /* compute the expected response to compare with what we received */ 00260 { 00261 char a2[256]; 00262 char a2_hash[256]; 00263 char resp[256]; 00264 00265 snprintf(a2, sizeof(a2), "%s:%s", sip_method2txt(sipmethod), S_OR(keys[K_URI].s, uri)); 00266 ast_md5_hash(a2_hash, a2); 00267 snprintf(resp, sizeof(resp), "%s:%s:%s", a1_hash, usednonce, a2_hash); 00268 ast_md5_hash(resp_hash, resp); 00269 } 00270 00271 good_response = keys[K_RESP].s && 00272 !strncasecmp(keys[K_RESP].s, resp_hash, strlen(resp_hash)); 00273 if (wrongnonce) { 00274 ast_string_field_build(p, randdata, "%08lx", ast_random()); 00275 if (good_response) { 00276 if (sipdebug) 00277 ast_log(LOG_NOTICE, "Correct auth, but based on stale nonce received from '%s'\n", get_header(req, "To")); 00278 /* We got working auth token, based on stale nonce . */ 00279 transmit_response_with_auth(p, response, req, p->randdata, reliable, respheader, 1); 00280 } else { 00281 /* Everything was wrong, so give the device one more try with a new challenge */ 00282 if (sipdebug) 00283 ast_log(LOG_NOTICE, "Bad authentication received from '%s'\n", get_header(req, "To")); 00284 transmit_response_with_auth(p, response, req, p->randdata, reliable, respheader, 0); 00285 } 00286 00287 /* Schedule auto destroy in 32 seconds */ 00288 sip_scheddestroy(p, DEFAULT_TRANS_TIMEOUT); 00289 return AUTH_CHALLENGE_SENT; 00290 } 00291 if (good_response) 00292 return AUTH_SUCCESSFUL; 00293 00294 /* Ok, we have a bad username/secret pair */ 00295 /* Challenge again, and again, and again */ 00296 transmit_response_with_auth(p, response, req, p->randdata, reliable, respheader, 0); 00297 sip_scheddestroy(p, DEFAULT_TRANS_TIMEOUT); 00298 00299 return AUTH_CHALLENGE_SENT; 00300 }
|
|
|
Clear realm authentication list (at reload).
Definition at line 554 of file sip3_auth.c. References free, and sip_auth::next. 00555 { 00556 struct sip_auth *a = authlist; 00557 struct sip_auth *b; 00558 00559 while (a) { 00560 b = a; 00561 a = a->next; 00562 free(b); 00563 } 00564 00565 return 1; 00566 }
|
|
||||||||||||||||||||||||
|
Add authentication on outbound SIP packet.
Definition at line 327 of file sip3_auth.c. References ast_calloc, ast_log(), sip_invite_param::auth, auth_headers(), sip_invite_param::authheader, sip_dialog::authtries, sip_dialog::inviteoptions, LOG_DEBUG, option_debug, reply_digest(), SIP_INVITE, sip_method2txt(), and transmit_invite(). 00328 { 00329 char *header, *respheader; 00330 char digest[1024]; 00331 00332 if (!p->inviteoptions && !(p->inviteoptions = ast_calloc(1, sizeof(*p->inviteoptions)))) 00333 return -2; 00334 00335 p->authtries++; 00336 auth_headers(code, &header, &respheader); 00337 if (option_debug > 1) 00338 ast_log(LOG_DEBUG, "Auth attempt %d on %s\n", p->authtries, sip_method2txt(sipmethod)); 00339 memset(digest, 0, sizeof(digest)); 00340 if (reply_digest(p, req, header, sipmethod, digest, sizeof(digest) )) { 00341 /* No way to authenticate */ 00342 return -1; 00343 } 00344 /* Now we have a reply digest */ 00345 p->inviteoptions->auth = digest; 00346 p->inviteoptions->authheader = respheader; 00347 return transmit_invite(p, sipmethod, sipmethod == SIP_INVITE, init); 00348 }
|
|
||||||||||||||||
|
Authenticate for outbound registration.
Definition at line 303 of file sip3_auth.c. References append_history, ast_test_flag, ast_verbose(), auth_headers(), sip_dialog::authtries, sip_dialog::flags, reply_digest(), sip_debug_test_pvt(), SIP_NO_HISTORY, SIP_REGISTER, and transmit_register(). 00304 { 00305 char *header, *respheader; 00306 char digest[1024]; 00307 00308 p->authtries++; 00309 auth_headers(code, &header, &respheader); 00310 memset(digest,0,sizeof(digest)); 00311 if (reply_digest(p, req, header, SIP_REGISTER, digest, sizeof(digest))) { 00312 /* There's nothing to use for authentication */ 00313 /* No digest challenge in request */ 00314 if (sip_debug_test_pvt(p) && p->registry) 00315 ast_verbose("No authentication challenge, sending blank registration to domain/host name %s\n", p->registry->hostname); 00316 /* No old challenge */ 00317 return -1; 00318 } 00319 if (!ast_test_flag(&p->flags[0], SIP_NO_HISTORY)) 00320 append_history(p, "RegistryAuth", "Try: %d", p->authtries); 00321 if (sip_debug_test_pvt(p) && p->registry) 00322 ast_verbose("Responding to challenge, registration to domain/host name %s\n", p->registry->hostname); 00323 return transmit_register(p->registry, SIP_REGISTER, digest, respheader); 00324 }
|
|
||||||||||||
|
Find authentication for a specific realm.
Definition at line 569 of file sip3_auth.c. References sip_auth::next, and sip_auth::realm. 00570 { 00571 struct sip_auth *a; 00572 00573 for (a = authlist; a; a = a->next) { 00574 if (!strcasecmp(a->realm, realm)) 00575 break; 00576 } 00577 00578 return a; 00579 }
|
|
||||||||||||||||||||||||||||
|
reply to authentication for outbound registrations
Definition at line 354 of file sip3_auth.c. References ast_log(), ast_string_field_index, ast_string_field_index_set, ast_strlen_zero(), get_header(), key(), keys, LOG_WARNING, and strsep(). 00355 { 00356 char tmp[512]; 00357 char *c; 00358 char oldnonce[256]; 00359 00360 /* table of recognised keywords, and places where they should be copied */ 00361 const struct keystruct { 00362 const char *key; 00363 int field_index; 00364 } *i, keys[] = { 00365 { "realm=", ast_string_field_index(p, realm) }, 00366 { "nonce=", ast_string_field_index(p, nonce) }, 00367 { "opaque=", ast_string_field_index(p, opaque) }, 00368 { "qop=", ast_string_field_index(p, qop) }, 00369 { "domain=", ast_string_field_index(p, domain) }, 00370 { NULL, 0 }, 00371 }; 00372 00373 ast_copy_string(tmp, get_header(req, header), sizeof(tmp)); 00374 if (ast_strlen_zero(tmp)) 00375 return -1; 00376 if (strncasecmp(tmp, "Digest ", strlen("Digest "))) { 00377 ast_log(LOG_WARNING, "missing Digest.\n"); 00378 return -1; 00379 } 00380 c = tmp + strlen("Digest "); 00381 ast_copy_string(oldnonce, p->nonce, sizeof(oldnonce)); 00382 while (c && *(c = ast_skip_blanks(c))) { /* lookup for keys */ 00383 for (i = keys; i->key != NULL; i++) { 00384 char *src, *separator; 00385 if (strncasecmp(c, i->key, strlen(i->key)) != 0) 00386 continue; 00387 /* Found. Skip keyword, take text in quotes or up to the separator. */ 00388 c += strlen(i->key); 00389 if (*c == '"') { 00390 src = ++c; 00391 separator = "\""; 00392 } else { 00393 src = c; 00394 separator = ","; 00395 } 00396 strsep(&c, separator); /* clear separator and move ptr */ 00397 ast_string_field_index_set(p, i->field_index, src); 00398 break; 00399 } 00400 if (i->key == NULL) /* not found, try ',' */ 00401 strsep(&c, ","); 00402 } 00403 /* Reset nonce count */ 00404 if (strcmp(p->nonce, oldnonce)) 00405 p->noncecount = 0; 00406 00407 /* Save auth data for following registrations */ 00408 if (p->registry) { 00409 struct sip_registry *r = p->registry; 00410 00411 if (strcmp(r->nonce, p->nonce)) { 00412 ast_string_field_set(r, realm, p->realm); 00413 ast_string_field_set(r, nonce, p->nonce); 00414 ast_string_field_set(r, domain, p->domain); 00415 ast_string_field_set(r, opaque, p->opaque); 00416 ast_string_field_set(r, qop, p->qop); 00417 r->noncecount = 0; 00418 } 00419 } 00420 return build_reply_digest(p, sipmethod, digest, digest_len); 00421 }
|
|
|
Definition at line 130 of file sip3_auth.c. |