static pjsip_authorization_hdr* pjsip_authorization_hdr_clone( pj_pool_t *pool, const pjsip_authorization_hdr *rhs) { /* This function also serves Proxy-Authorization header. */ pjsip_authorization_hdr *hdr; if (rhs->type == PJSIP_H_AUTHORIZATION) hdr = pjsip_authorization_hdr_create(pool); else hdr = pjsip_proxy_authorization_hdr_create(pool); pj_strdup(pool, &hdr->scheme, &rhs->scheme); if (pj_stricmp2(&hdr->scheme, "digest") == 0) { pj_strdup(pool, &hdr->credential.digest.username, &rhs->credential.digest.username); pj_strdup(pool, &hdr->credential.digest.realm, &rhs->credential.digest.realm); pj_strdup(pool, &hdr->credential.digest.nonce, &rhs->credential.digest.nonce); pj_strdup(pool, &hdr->credential.digest.uri, &rhs->credential.digest.uri); pj_strdup(pool, &hdr->credential.digest.response, &rhs->credential.digest.response); pj_strdup(pool, &hdr->credential.digest.algorithm, &rhs->credential.digest.algorithm); pj_strdup(pool, &hdr->credential.digest.cnonce, &rhs->credential.digest.cnonce); pj_strdup(pool, &hdr->credential.digest.opaque, &rhs->credential.digest.opaque); pj_strdup(pool, &hdr->credential.digest.qop, &rhs->credential.digest.qop); pj_strdup(pool, &hdr->credential.digest.nc, &rhs->credential.digest.nc); pjsip_param_clone(pool, &hdr->credential.digest.other_param, &rhs->credential.digest.other_param); } else if (pj_stricmp2(&hdr->scheme, "pgp") == 0) { pj_assert(0); return NULL; } else { pj_assert(0); return NULL; } return hdr; }
void PJUtils::add_integrity_protected_indication(pjsip_tx_data* tdata, Integrity integrity) { pjsip_authorization_hdr* auth_hdr = (pjsip_authorization_hdr*) pjsip_msg_find_hdr(tdata->msg, PJSIP_H_AUTHORIZATION, NULL); if (auth_hdr == NULL) { auth_hdr = pjsip_authorization_hdr_create(tdata->pool); auth_hdr->scheme = pj_str("Digest"); // Construct a default private identifier from the URI in the To header. LOG_DEBUG("Construct default private identity"); pjsip_uri* to_uri = (pjsip_uri*)pjsip_uri_get_uri(PJSIP_MSG_TO_HDR(tdata->msg)->uri); std::string private_id = PJUtils::default_private_id_from_uri(to_uri); pj_strdup2(tdata->pool, &auth_hdr->credential.digest.username, private_id.c_str()); pjsip_msg_add_hdr(tdata->msg, (pjsip_hdr*)auth_hdr); } pjsip_param* new_param = (pjsip_param*) pj_pool_alloc(tdata->pool, sizeof(pjsip_param)); new_param->name = STR_INTEGRITY_PROTECTED; switch (integrity) { case Integrity::YES: new_param->value = STR_YES; break; case Integrity::NO: new_param->value = STR_NO; break; case Integrity::TLS_YES: new_param->value = STR_TLS_YES; break; case Integrity::TLS_PENDING: new_param->value = STR_TLS_PENDING; break; case Integrity::IP_ASSOC_YES: new_param->value = STR_IP_ASSOC_YES; break; case Integrity::IP_ASSOC_PENDING: new_param->value = STR_IP_ASSOC_PENDING; break; case Integrity::AUTH_DONE: new_param->value = STR_AUTH_DONE; break; default: break; } LOG_INFO("Adding integrity-protected=%.*s indicator to message", new_param->value.slen, new_param->value.ptr); pj_list_insert_before(&auth_hdr->credential.common.other_param, new_param); }
void PJUtils::add_integrity_protected_indication(pjsip_tx_data* tdata, Integrity integrity) { pjsip_authorization_hdr* auth_hdr = (pjsip_authorization_hdr*) pjsip_msg_find_hdr(tdata->msg, PJSIP_H_AUTHORIZATION, NULL); if (auth_hdr == NULL) { auth_hdr = pjsip_authorization_hdr_create(tdata->pool); auth_hdr->scheme = pj_str("Digest"); auth_hdr->credential.digest.realm = stack_data.home_domain; auth_hdr->credential.digest.username = PJUtils::uri_to_pj_str(PJSIP_URI_IN_FROMTO_HDR, tdata->msg->line.req.uri, tdata->pool); pjsip_msg_add_hdr(tdata->msg, (pjsip_hdr*)auth_hdr); } pjsip_param* new_param = (pjsip_param*) pj_pool_alloc(tdata->pool, sizeof(pjsip_param)); new_param->name = STR_INTEGRITY_PROTECTED; switch (integrity) { case Integrity::YES: new_param->value = STR_YES; break; case Integrity::NO: new_param->value = STR_NO; break; case Integrity::TLS_YES: new_param->value = STR_TLS_YES; break; case Integrity::TLS_PENDING: new_param->value = STR_TLS_PENDING; break; case Integrity::IP_ASSOC_YES: new_param->value = STR_IP_ASSOC_YES; break; case Integrity::IP_ASSOC_PENDING: new_param->value = STR_IP_ASSOC_PENDING; break; case Integrity::AUTH_DONE: new_param->value = STR_AUTH_DONE; break; default: break; } LOG_INFO("Adding integrity-protected=%.*s indicator to message", new_param->value.slen, new_param->value.ptr); pj_list_insert_before(&auth_hdr->credential.common.other_param, new_param); }
/// Adds a header indicating the message is integrity protected because it /// was received on a transport that has already been authenticated. void PJUtils::add_integrity_protected_indication(pjsip_tx_data* tdata) { LOG_INFO("Adding integrity-protected indicator to message"); pjsip_authorization_hdr* auth_hdr = (pjsip_authorization_hdr*) pjsip_msg_find_hdr(tdata->msg, PJSIP_H_AUTHORIZATION, NULL); if (auth_hdr == NULL) { auth_hdr = pjsip_authorization_hdr_create(tdata->pool); auth_hdr->scheme = pj_str("Digest"); auth_hdr->credential.digest.realm = pj_str(""); auth_hdr->credential.digest.username = PJUtils::uri_to_pj_str(PJSIP_URI_IN_FROMTO_HDR, tdata->msg->line.req.uri, tdata->pool); pjsip_msg_add_hdr(tdata->msg, (pjsip_hdr*)auth_hdr); } pjsip_param* new_param = (pjsip_param*) pj_pool_alloc(tdata->pool, sizeof(pjsip_param)); new_param->name = STR_INTEGRITY_PROTECTED; new_param->value = pj_str("\"yes\""); pj_list_insert_before(&auth_hdr->credential.common.other_param, new_param); }
static pjsip_hdr* parse_hdr_authorization( pjsip_parse_ctx *ctx ) { pjsip_authorization_hdr *hdr = pjsip_authorization_hdr_create(ctx->pool); int_parse_hdr_authorization(ctx->scanner, ctx->pool, hdr); return (pjsip_hdr*)hdr; }
/* Initialize outgoing request. */ PJ_DEF(pj_status_t) pjsip_auth_clt_init_req( pjsip_auth_clt_sess *sess, pjsip_tx_data *tdata ) { const pjsip_method *method; pjsip_cached_auth *auth; pjsip_hdr added; PJ_ASSERT_RETURN(sess && tdata, PJ_EINVAL); PJ_ASSERT_RETURN(sess->pool, PJSIP_ENOTINITIALIZED); PJ_ASSERT_RETURN(tdata->msg->type==PJSIP_REQUEST_MSG, PJSIP_ENOTREQUESTMSG); /* Init list */ pj_list_init(&added); /* Get the method. */ method = &tdata->msg->line.req.method; auth = sess->cached_auth.next; while (auth != &sess->cached_auth) { /* Reset stale counter */ auth->stale_cnt = 0; if (auth->qop_value == PJSIP_AUTH_QOP_NONE) { # if defined(PJSIP_AUTH_HEADER_CACHING) && \ PJSIP_AUTH_HEADER_CACHING!=0 { pjsip_cached_auth_hdr *entry = auth->cached_hdr.next; while (entry != &auth->cached_hdr) { if (pjsip_method_cmp(&entry->method, method)==0) { pjsip_authorization_hdr *hauth; hauth = pjsip_hdr_shallow_clone(tdata->pool, entry->hdr); //pjsip_msg_add_hdr(tdata->msg, (pjsip_hdr*)hauth); pj_list_push_back(&added, hauth); break; } entry = entry->next; } # if defined(PJSIP_AUTH_AUTO_SEND_NEXT) && \ PJSIP_AUTH_AUTO_SEND_NEXT!=0 { if (entry == &auth->cached_hdr) new_auth_for_req( tdata, sess, auth, NULL); } # endif } # elif defined(PJSIP_AUTH_AUTO_SEND_NEXT) && \ PJSIP_AUTH_AUTO_SEND_NEXT!=0 { new_auth_for_req( tdata, sess, auth, NULL); } # endif } # if defined(PJSIP_AUTH_QOP_SUPPORT) && \ defined(PJSIP_AUTH_AUTO_SEND_NEXT) && \ (PJSIP_AUTH_QOP_SUPPORT && PJSIP_AUTH_AUTO_SEND_NEXT) else if (auth->qop_value == PJSIP_AUTH_QOP_AUTH) { /* For qop="auth", we have to re-create the authorization header. */ const pjsip_cred_info *cred; pjsip_authorization_hdr *hauth; pj_status_t status; cred = auth_find_cred(sess, &auth->realm, &auth->last_chal->scheme); if (!cred) { auth = auth->next; continue; } status = auth_respond( tdata->pool, auth->last_chal, tdata->msg->line.req.uri, cred, &tdata->msg->line.req.method, sess->pool, auth, &hauth); if (status != PJ_SUCCESS) return status; //pjsip_msg_add_hdr(tdata->msg, (pjsip_hdr*)hauth); pj_list_push_back(&added, hauth); } # endif /* PJSIP_AUTH_QOP_SUPPORT && PJSIP_AUTH_AUTO_SEND_NEXT */ auth = auth->next; } if (sess->pref.initial_auth == PJ_FALSE) { pjsip_hdr *h; /* Don't want to send initial empty Authorization header, so * just send whatever available in the list (maybe empty). */ h = added.next; while (h != &added) { pjsip_hdr *next = h->next; pjsip_msg_add_hdr(tdata->msg, h); h = next; } } else { /* For each realm, add either the cached authorization header * or add an empty authorization header. */ unsigned i; char *uri_str; int len; uri_str = (char*)pj_pool_alloc(tdata->pool, PJSIP_MAX_URL_SIZE); len = pjsip_uri_print(PJSIP_URI_IN_REQ_URI, tdata->msg->line.req.uri, uri_str, PJSIP_MAX_URL_SIZE); if (len < 1 || len >= PJSIP_MAX_URL_SIZE) return PJSIP_EURITOOLONG; for (i=0; i<sess->cred_cnt; ++i) { pjsip_cred_info *c = &sess->cred_info[i]; pjsip_authorization_hdr *h; h = get_header_for_realm(&added, &c->realm); if (h) { pj_list_erase(h); pjsip_msg_add_hdr(tdata->msg, (pjsip_hdr*)h); } else { pjsip_authorization_hdr *hs; hs = pjsip_authorization_hdr_create(tdata->pool); pj_strdup(tdata->pool, &hs->scheme, &c->scheme); pj_strdup(tdata->pool, &hs->credential.digest.username, &c->username); pj_strdup(tdata->pool, &hs->credential.digest.realm, &c->realm); pj_strdup2(tdata->pool, &hs->credential.digest.uri, uri_str); pj_strdup(tdata->pool, &hs->credential.digest.algorithm, &sess->pref.algorithm); pjsip_msg_add_hdr(tdata->msg, (pjsip_hdr*)hs); } } } return PJ_SUCCESS; }
/* * Create Authorization/Proxy-Authorization response header based on the challege * in WWW-Authenticate/Proxy-Authenticate header. */ static pj_status_t auth_respond( pj_pool_t *req_pool, const pjsip_www_authenticate_hdr *hdr, const pjsip_uri *uri, const pjsip_cred_info *cred_info, const pjsip_method *method, pj_pool_t *sess_pool, pjsip_cached_auth *cached_auth, pjsip_authorization_hdr **p_h_auth) { pjsip_authorization_hdr *hauth; char tmp[PJSIP_MAX_URL_SIZE]; pj_str_t uri_str; pj_pool_t *pool; pj_status_t status; /* Verify arguments. */ PJ_ASSERT_RETURN(req_pool && hdr && uri && cred_info && method && sess_pool && cached_auth && p_h_auth, PJ_EINVAL); /* Print URL in the original request. */ uri_str.ptr = tmp; uri_str.slen = pjsip_uri_print(PJSIP_URI_IN_REQ_URI, uri, tmp,sizeof(tmp)); if (uri_str.slen < 1) { pj_assert(!"URL is too long!"); return PJSIP_EURITOOLONG; } # if (PJSIP_AUTH_HEADER_CACHING) { pool = sess_pool; PJ_UNUSED_ARG(req_pool); } # else { pool = req_pool; PJ_UNUSED_ARG(sess_pool); } # endif if (hdr->type == PJSIP_H_WWW_AUTHENTICATE) hauth = pjsip_authorization_hdr_create(pool); else if (hdr->type == PJSIP_H_PROXY_AUTHENTICATE) hauth = pjsip_proxy_authorization_hdr_create(pool); else { pj_assert(!"Invalid response header!"); return PJSIP_EINVALIDHDR; } /* Only support digest scheme at the moment. */ if (!pj_stricmp(&hdr->scheme, &pjsip_DIGEST_STR)) { pj_str_t *cnonce = NULL; pj_uint32_t nc = 1; /* Update the session (nonce-count etc) if required. */ # if PJSIP_AUTH_QOP_SUPPORT { if (cached_auth) { update_digest_session( sess_pool, cached_auth, hdr ); cnonce = &cached_auth->cnonce; nc = cached_auth->nc; } } # endif /* PJSIP_AUTH_QOP_SUPPORT */ hauth->scheme = pjsip_DIGEST_STR; status = respond_digest( pool, &hauth->credential.digest, &hdr->challenge.digest, &uri_str, cred_info, cnonce, nc, &method->name); if (status != PJ_SUCCESS) return status; /* Set qop type in auth session the first time only. */ if (hdr->challenge.digest.qop.slen != 0 && cached_auth) { if (cached_auth->qop_value == PJSIP_AUTH_QOP_NONE) { pj_str_t *qop_val = &hauth->credential.digest.qop; if (!pj_strcmp(qop_val, &pjsip_AUTH_STR)) { cached_auth->qop_value = PJSIP_AUTH_QOP_AUTH; } else { cached_auth->qop_value = PJSIP_AUTH_QOP_UNKNOWN; } } } } else { return PJSIP_EINVALIDAUTHSCHEME; } /* Keep the new authorization header in the cache, only * if no qop is not present. */ # if PJSIP_AUTH_HEADER_CACHING { if (hauth && cached_auth && cached_auth->qop_value == PJSIP_AUTH_QOP_NONE) { pjsip_cached_auth_hdr *cached_hdr; /* Delete old header with the same method. */ cached_hdr = cached_auth->cached_hdr.next; while (cached_hdr != &cached_auth->cached_hdr) { if (pjsip_method_cmp(method, &cached_hdr->method)==0) break; cached_hdr = cached_hdr->next; } /* Save the header to the list. */ if (cached_hdr != &cached_auth->cached_hdr) { cached_hdr->hdr = hauth; } else { cached_hdr = pj_pool_alloc(pool, sizeof(*cached_hdr)); pjsip_method_copy( pool, &cached_hdr->method, method); cached_hdr->hdr = hauth; pj_list_insert_before( &cached_auth->cached_hdr, cached_hdr ); } } # if defined(PJSIP_AUTH_AUTO_SEND_NEXT) && PJSIP_AUTH_AUTO_SEND_NEXT!=0 if (hdr != cached_auth->last_chal) { cached_auth->last_chal = pjsip_hdr_clone(sess_pool, hdr); } # endif } # endif *p_h_auth = hauth; return PJ_SUCCESS; }
pjsip_auth_respond( pj_pool_t *req_pool, const pjsip_www_authenticate_hdr *hdr, const pjsip_uri *uri, const pjsip_cred_info *cred_info, const pjsip_method *method, pj_pool_t *sess_pool, pjsip_auth_session *auth_sess) { pjsip_authorization_hdr *auth; char tmp[PJSIP_MAX_URL_SIZE]; pj_str_t uri_str; pj_pool_t *pool; pj_assert(hdr != NULL); pj_assert(uri != NULL); pj_assert(cred_info != NULL); pj_assert(method != NULL); /* Print URL in the original request. */ uri_str.ptr = tmp; uri_str.slen = pjsip_uri_print(PJSIP_URI_IN_REQ_URI, uri, tmp, sizeof(tmp)); if (uri_str.slen < 1) { pj_assert(!"URL is too long!"); PJ_LOG(4,(THIS_FILE, "Unable to authorize: URI is too long!")); return NULL; } # if (PJSIP_AUTH_HEADER_CACHING) { pool = sess_pool; PJ_UNUSED_ARG(req_pool); } # else { pool = req_pool; PJ_UNUSED_ARG(sess_pool); } # endif if (hdr->type == PJSIP_H_WWW_AUTHENTICATE) auth = pjsip_authorization_hdr_create(pool); else if (hdr->type == PJSIP_H_PROXY_AUTHENTICATE) auth = pjsip_proxy_authorization_hdr_create(pool); else { pj_assert(0); return NULL; } /* Only support digest scheme at the moment. */ if (!pj_stricmp(&hdr->scheme, &pjsip_DIGEST_STR)) { pj_status_t rc; pj_str_t *cnonce = NULL; pj_uint32_t nc = 1; /* Update the session (nonce-count etc) if required. */ # if PJSIP_AUTH_QOP_SUPPORT { if (auth_sess) { update_digest_session( sess_pool, auth_sess, hdr ); cnonce = &auth_sess->cnonce; nc = auth_sess->nc; } } # endif /* PJSIP_AUTH_QOP_SUPPORT */ auth->scheme = pjsip_DIGEST_STR; rc = respond_digest( pool, &auth->credential.digest, &hdr->challenge.digest, &uri_str, cred_info, cnonce, nc, &method->name); if (rc != 0) return NULL; /* Set qop type in auth session the first time only. */ if (hdr->challenge.digest.qop.slen != 0 && auth_sess) { if (auth_sess->qop_value == PJSIP_AUTH_QOP_NONE) { pj_str_t *qop_val = &auth->credential.digest.qop; if (!pj_strcmp(qop_val, &pjsip_AUTH_STR)) { auth_sess->qop_value = PJSIP_AUTH_QOP_AUTH; } else { auth_sess->qop_value = PJSIP_AUTH_QOP_UNKNOWN; } } } } else { auth = NULL; } /* Keep the new authorization header in the cache, only * if no qop is not present. */ # if PJSIP_AUTH_HEADER_CACHING { if (auth && auth_sess && auth_sess->qop_value == PJSIP_AUTH_QOP_NONE) { pjsip_cached_auth_hdr *cached_hdr; /* Delete old header with the same method. */ cached_hdr = auth_sess->cached_hdr.next; while (cached_hdr != &auth_sess->cached_hdr) { if (pjsip_method_cmp(method, &cached_hdr->method)==0) break; cached_hdr = cached_hdr->next; } /* Save the header to the list. */ if (cached_hdr != &auth_sess->cached_hdr) { cached_hdr->hdr = auth; } else { cached_hdr = pj_pool_alloc(pool, sizeof(*cached_hdr)); pjsip_method_copy( pool, &cached_hdr->method, method); cached_hdr->hdr = auth; pj_list_insert_before( &auth_sess->cached_hdr, cached_hdr ); } } } # endif return auth; }