/** Get session mode from attribute list. */ sdp_mode_t sdp_attribute_mode(sdp_attribute_t const *a, sdp_mode_t defmode) { for (; a; a = a->a_next) { if (su_casematch(a->a_name, "sendrecv")) return sdp_sendrecv; if (su_casematch(a->a_name, "inactive")) return sdp_inactive; if (su_casematch(a->a_name, "recvonly")) return sdp_recvonly; if (su_casematch(a->a_name, "sendonly")) return sdp_sendonly; } return defmode; }
/**Replace or append a attribute within a list of attributes. * * @retval 1 if replaced existing attribute * @retval 0 if attribute was appended * @retval -1 upon an error */ int sdp_attribute_replace(sdp_attribute_t **list, sdp_attribute_t *a, sdp_attribute_t **return_replaced) { sdp_attribute_t *replaced; assert(list); if (return_replaced) *return_replaced = NULL; if (list == NULL || a == NULL) return -1; assert(a->a_name != NULL); assert(a->a_next == NULL); for (; *list; list = &(*list)->a_next) { if (su_casematch((*list)->a_name, a->a_name)) break; } replaced = *list, *list = a; if (replaced) { a->a_next = replaced->a_next; replaced->a_next = NULL; if (return_replaced) *return_replaced = replaced; return 1; } return 0; }
static int soa_static_set_params(soa_session_t *ss, tagi_t const *tags) { soa_static_session_t *sss = (soa_static_session_t *)ss; char const *audio_aux = sss->sss_audio_aux; int ordered_user = sss->sss_ordered_user; int reuse_rejected = sss->sss_reuse_rejected; int n, m; n = tl_gets(tags, SOATAG_AUDIO_AUX_REF(audio_aux), SOATAG_ORDERED_USER_REF(ordered_user), SOATAG_REUSE_REJECTED_REF(reuse_rejected), TAG_END()); if (n > 0 && !su_casematch(audio_aux, sss->sss_audio_aux)) { char *s = su_strdup(ss->ss_home, audio_aux), *tbf = sss->sss_audio_aux; if (s == NULL && audio_aux != NULL) return -1; sss->sss_audio_aux = s; if (tbf) su_free(ss->ss_home, tbf); } sss->sss_ordered_user = ordered_user != 0; sss->sss_reuse_rejected = reuse_rejected != 0; m = soa_base_set_params(ss, tags); if (m < 0) return m; return n + m; }
static int nua_refer_server_respond(nua_server_request_t *sr, tagi_t const *tags) { nua_handle_t *nh = sr->sr_owner; struct notifier_usage *nu = nua_dialog_usage_private(sr->sr_usage); sip_refer_sub_t const *rs = sip_refer_sub(sr->sr_response.sip); if (sr->sr_status < 200 || nu == NULL) { } else if (sr->sr_status < 300 && /* No subscription if Refer-Sub: false in response */ (rs == NULL || !su_casematch(rs->rs_value, "false"))) { sr->sr_usage->du_ready = 1; nu->nu_expires = sip_now() + NH_PGET(nh, refer_expires); if (sr->sr_application) /* Application responded to REFER */ nu->nu_substate = nua_substate_active; } else { /* Destroy the implicit subscription usage */ sr->sr_terminating = 1; } return nua_base_server_respond(sr, tags); }
static void print_attributes_without_mode(sdp_printer_t *p, sdp_attribute_t const *a) { for (;a; a = a->a_next) { char const *name = a->a_name; char const *value = a->a_value; if (su_casematch(name, "inactive") || su_casematch(name, "sendonly") || su_casematch(name, "recvonly") || su_casematch(name, "sendrecv")) continue; sdp_printf(p, "a=%s%s%s" CRLF, name, value ? ":" : "", value ? value : ""); } }
static int nua_refer_client_response(nua_client_request_t *cr, int status, char const *phrase, sip_t const *sip) { nua_dialog_usage_t *du = cr->cr_usage; enum nua_substate substate = nua_substate_terminated; if (du) { struct event_usage *eu = nua_dialog_usage_private(du); if (status < 200) { substate = eu->eu_substate; } else if (status < 300) { sip_refer_sub_t const *rs = sip_refer_sub(sip); if (rs && su_casematch("false", rs->rs_value)) cr->cr_terminated = 1; if (!cr->cr_terminated) substate = eu->eu_substate; } } return nua_base_client_tresponse(cr, status, phrase, sip, NUTAG_SUBSTATE(substate), SIPTAG_EVENT(du ? du->du_event : NULL), TAG_END()); }
static int nua_subscribe_server_report(nua_server_request_t *sr, tagi_t const *tags) { nua_handle_t *nh = sr->sr_owner; nua_dialog_state_t *ds = nh->nh_ds; nua_dialog_usage_t *du = sr->sr_usage; struct notifier_usage *nu = nua_dialog_usage_private(du); enum nua_substate substate = nua_substate_terminated; int notify = 0; int retval; if (nu && !sr->sr_terminating) { substate = nu->nu_substate; } /* nu_requested is set by SUBSCRIBE and cleared when NOTIFY is sent */ if (nu && nu->nu_requested && substate != nua_substate_embryonic) { #if SU_HAVE_EXPERIMENTAL sip_t const *sip = sr->sr_request.sip; sip_suppress_notify_if_match_t *snim = sip_suppress_notify_if_match(sip); sip_suppress_body_if_match_t *sbim = sip_suppress_body_if_match(sip); if (!nu->nu_tag) notify = 1; else if (snim && su_casematch(snim->snim_tag, nu->nu_tag)) notify = 0; else if (sbim && su_casematch(snim->snim_tag, nu->nu_tag)) notify = 1, nu->nu_no_body = 1; else #endif notify = 1; notify = notify && du->du_cr != NULL; } retval = nua_base_server_treport(sr, NUTAG_SUBSTATE(substate), TAG_END()); if (retval >= 2 || du == NULL) return retval; if (notify) { /* Send NOTIFY (and terminate subscription, when needed) */ nua_dialog_usage_refresh(nh, ds, du); } return retval; }
/** Find named attribute from given lists (a or a2). */ sdp_attribute_t *sdp_attribute_find2(sdp_attribute_t const *a, sdp_attribute_t const *a2, char const *name) { for (; a; a = a->a_next) { if (su_casematch(a->a_name, name)) break; } if (a == 0) for (a = a2; a; a = a->a_next) { if (su_casematch(a->a_name, name)) break; } return (sdp_attribute_t *)a; }
/** Check if request should be processed by outbound */ int outbound_targeted_request(sip_t const *sip) { return sip && sip->sip_request && sip->sip_request->rq_method == sip_method_options && sip->sip_accept && sip->sip_accept->ac_type && su_casematch(sip->sip_accept->ac_type, outbound_content_type); }
/** Decode (parse) @ReferSub header */ issize_t sip_refer_sub_d(su_home_t *home, sip_header_t *h, char *s, isize_t slen) { sip_refer_sub_t *rs = (sip_refer_sub_t *)h; if (msg_token_d(&s, &rs->rs_value) < 0) return -1; if (!su_casematch(rs->rs_value, "false") && !su_casematch(rs->rs_value, "true")) return -1; if (*s) if (msg_params_d(home, &s, &rs->rs_params) == -1) return -1; return s - rs->rs_value; }
int count_transports(struct dig *dig, char const *tport, char const *tport2) { int i, tcount = 0; struct transport const *tports = dig->tports; for (i = 0; tports[i].name; i++) { if (dig->sips && !transport_is_secure(tports[i].name)) continue; if (!tport || su_casematch(tport, tports[i].name)) tcount++; else if (tport2 && su_casematch(tport2, tports[i].name)) tcount++; } return tcount; }
/** Parse SIP version. * * Parse a SIP version string. Update the * pointer at @a ss to first non-LWS character after the version string. * * @param ss string to be parsed [IN/OUT] * @param ver value result for version [OUT] * * @retval 0 when successful, * @retval -1 upon an error. */ int sip_version_d(char **ss, char const **ver) { char *s = *ss; char const *result; size_t const version_size = sizeof(sip_version_2_0) - 1; if (su_casenmatch(s, sip_version_2_0, version_size) && !IS_TOKEN(s[version_size])) { result = sip_version_2_0; s += version_size; } else { /* Version consists of two tokens, separated by / */ size_t l1 = 0, l2 = 0, n; result = s; l1 = span_token(s); for (n = l1; IS_LWS(s[n]); n++) {} if (s[n] == '/') { for (n++; IS_LWS(s[n]); n++) {} l2 = span_token(s + n); n += l2; } if (l1 == 0 || l2 == 0) return -1; /* If there is extra ws between tokens, compact version */ if (n > l1 + 1 + l2) { s[l1] = '/'; memmove(s + l1 + 1, s + n - l2, l2); s[l1 + 1 + l2] = 0; /* Compare again with compacted version */ if (su_casematch(s, sip_version_2_0)) result = sip_version_2_0; } s += n; } while (IS_WS(*s)) *s++ = '\0'; *ss = s; if (ver) *ver = result; return 0; }
struct transport const * transport_by_service(struct transport const *tports, char const *s) { int i; for (i = 0; tports[i].name; i++) { if (su_casematch(tports[i].service, s)) return tports + i; } return NULL; }
/** Duplicate a transport string */ void sip_transport_dup(char **pp, char const **dd, char const *s) { if (s == sip_transport_udp) *dd = s; else if (s == sip_transport_tcp) *dd = s; else if (s == sip_transport_sctp) *dd = s; else if (s == sip_transport_tls) *dd = s; else if (s == sip_transport_ws) *dd = s; else if (s == sip_transport_wss) *dd = s; else if (su_casematch(s, sip_transport_udp)) *dd = sip_transport_udp; else if (su_casematch(s, sip_transport_tcp)) *dd = sip_transport_tcp; else if (su_casematch(s, sip_transport_sctp)) *dd = sip_transport_sctp; else if (su_casematch(s, sip_transport_tls)) *dd = sip_transport_tls; else if (su_casematch(s, sip_transport_ws)) *dd = sip_transport_ws; else if (su_casematch(s, sip_transport_wss)) *dd = sip_transport_wss; else MSG_STRING_DUP(*pp, *dd, s); }
/** Compare @SecurityVerify header with @SecurityServer header. */ int sip_security_verify_compare(sip_security_server_t const *s, sip_security_verify_t const *v, msg_param_t *return_d_ver) { size_t i, j; int retval, digest; msg_param_t const *s_params, *v_params, empty[] = { NULL }; if (return_d_ver) *return_d_ver = NULL; if (s == NULL) return 0; for (;;s = s->sa_next, v = v->sa_next) { if (s == NULL || v == NULL) return (s == NULL) - (v == NULL); if ((retval = su_strcmp(s->sa_mec, v->sa_mec))) return retval; digest = su_casematch(s->sa_mec, "Digest"); s_params = s->sa_params, v_params = v->sa_params; if (digest && s_params == NULL && v_params != NULL) s_params = empty; if (s_params == NULL || v_params == NULL) { if ((retval = (s_params == NULL) - (v_params == NULL))) return retval; continue; } for (i = 0, j = 0;; i++, j++) { if (digest && v_params[j] && su_casenmatch(v_params[j], "d-ver=", 6)) { if (return_d_ver) *return_d_ver = v_params[j] + strlen("d-ver="); j++; } retval = su_strcmp(s_params[i], v_params[j]); if (retval || s_params[i] == NULL || v_params[j] == NULL) break; } if (retval) return retval; } }
/**Get ntlm-challenge parameters. * * The function ntlm_challenge_get() searches for the ntlm authentication * parameters in @a params. The parameters are assigned to the appropriate * fields in @a ac structure. * * @return * * The function ntlm_challenge_get() returns number of parameters * found, or -1 upon an error. */ issize_t auth_ntlm_challenge_get(su_home_t *home, auth_challenge_t *ac0, char const * const params[]) { ssize_t n; auth_challenge_t ac[1] = {{ 0 }}; char const *md5 = NULL, *md5sess = NULL, *sha1 = NULL, *qop_auth = NULL, *qop_auth_int = NULL; ac->ac_size = sizeof(ac); assert(ac0); assert(ac0->ac_size >= sizeof(*ac)); if (ac0 == NULL || params == NULL) return -1; n = auth_get_params(home, params, "realm=", &ac->ac_realm, "domain=", &ac->ac_domain, "nonce=", &ac->ac_nonce, "opaque=", &ac->ac_opaque, "stale=", &ac->ac_stale, "algorithm=", &ac->ac_algorithm, "qop=", &ac->ac_qop, "algorithm=md5", &md5, "algorithm=md5-sess", &md5sess, "algorithm=sha1", &sha1, "qop=auth", &qop_auth, "qop=auth-int", &qop_auth_int, NULL); if (n < 0) return n; if (ac->ac_stale && !su_casematch(ac->ac_stale, "true")) ac->ac_stale = NULL; ac->ac_md5 = md5 != NULL || ac->ac_algorithm == NULL; ac->ac_md5sess = md5sess != NULL; ac->ac_sha1 = sha1 != NULL; ac->ac_auth = qop_auth != NULL; ac->ac_auth_int = qop_auth_int != NULL; auth_struct_copy(ac0, ac, sizeof(ac)); SU_DEBUG_5(("%s(): got %d\n", "auth_ntlm_challenge_get", n)); return n; }
/* Return 1 if media type and protocol of m= line structs matches */ unsigned sdp_media_match_with(sdp_media_t const *a, sdp_media_t const *b) { if (a == NULL || b == NULL) return a == b; if (a->m_type == sdp_media_any || b->m_type == sdp_media_any) return 1; if (a->m_type != b->m_type || (a->m_type == sdp_media_x && !su_casematch(b->m_type_name, a->m_type_name))) return 0; if (a->m_proto == sdp_proto_any || b->m_proto == sdp_proto_any) return 1; if (a->m_proto != b->m_proto || (a->m_proto == sdp_proto_x && !su_casematch(b->m_proto_name, a->m_proto_name))) return 0; return 1; }
/** Convert a @Via header to @Contact header. * * The @Contact URI will contain the port number if needed. If transport * protocol name starts with "TLS", "SIPS:" URI schema is used. Transport * parameter is included in the URI unless the transport protocol is UDP. * * @param home memory home * @param v @Via header field structure * (with <sent-protocol> and <sent-by> parameters) * @param user username for @Contact URI (may be NULL) * * @retval contact header structure * @retval NULL upon an error * * @sa sip_contact_create_from_via_with_transport(), * sip_contact_string_from_via() */ sip_contact_t * sip_contact_create_from_via(su_home_t *home, sip_via_t const *v, char const *user) { const char *tp; if (!v) return NULL; tp = v->v_protocol; if (tp == sip_transport_udp || su_casematch(tp, sip_transport_udp)) /* Default is UDP */ tp = NULL; return sip_contact_create_from_via_with_transport(home, v, user, tp); }
static int test_casematch(void) { BEGIN(); TEST_1(!su_casematch(NULL, "")); TEST_1(su_casematch(NULL, NULL)); TEST_1(!su_casematch("", NULL)); TEST_1(!su_casenmatch(NULL, "", 1)); TEST_1(su_casenmatch(NULL, NULL, 1)); TEST_1(!su_casenmatch("", NULL, 1)); TEST_1(su_casenmatch(NULL, "", 0)); TEST_1(su_casenmatch(NULL, NULL, 0)); TEST_1(su_casenmatch("", NULL, 0)); TEST_1(su_casenmatch("foo", "foo", 3)); TEST_1(su_casenmatch("FOO", "foo", 3)); TEST_1(su_casenmatch("foo", "FOO", 3)); TEST_1(su_casenmatch("foo", "foo", 4)); TEST_1(su_casenmatch("FOO", "foo", 4)); TEST_1(su_casenmatch("foo", "FOO", 4)); TEST_1(su_casematch("foo", "foo")); TEST_1(su_casematch("FOO", "foo")); TEST_1(su_casematch("foo", "FOO")); TEST_1(!su_casematch("foo_", "foo")); TEST_1(!su_casematch("FOO_", "foo")); TEST_1(!su_casematch("foo_", "FOO")); TEST_1(su_casenmatch("foo\0X", "foo\0z", 5)); TEST_1(su_casenmatch("FOO\0X", "foo\0z", 5)); TEST_1(su_casenmatch("foo\0X", "FOO\0z", 5)); END(); }
static int li_name(su_localinfo_t const *hints, int gni_flags, su_sockaddr_t const *su, char **ccanonname) { char name[SU_MAXHOST]; int error; int flags = hints->li_flags; *ccanonname = NULL; if ((flags & LI_CANONNAME) || hints->li_canonname) { if ((flags & LI_NAMEREQD) == LI_NAMEREQD) gni_flags |= NI_NAMEREQD; if (flags & LI_NUMERIC) gni_flags |= NI_NUMERICHOST; error = su_getnameinfo(su, su_sockaddr_size(su), name, sizeof(name), NULL, 0, gni_flags); if (error) { if ((flags & LI_NAMEREQD) == LI_NAMEREQD) return 1; SU_DEBUG_7(("li_name: getnameinfo() failed\n")); if (!su_inet_ntop(su->su_family, SU_ADDR(su), name, sizeof name)) return ELI_RESOLVER; } if (hints->li_canonname && !su_casematch(name, hints->li_canonname)) return 1; if (!(flags & LI_CANONNAME)) return 0; if (!(*ccanonname = strdup(name))) return ELI_MEMORY; } return 0; }
/** Find a credential header with matching scheme and realm. */ msg_auth_t *auth_mod_credentials(msg_auth_t *auth, char const *scheme, char const *realm) { char const *arealm; for (; auth; auth = auth->au_next) { if (!su_casematch(auth->au_scheme, scheme)) continue; if (!realm) return auth; arealm = msg_header_find_param(auth->au_common, "realm="); if (!arealm) continue; if (arealm[0] == '"') { /* Compare quoted arealm with unquoted realm */ int i, j; for (i = 1, j = 0; arealm[i] != 0; i++, j++) { if (arealm[i] == '"' && realm[j] == 0) return auth; if (arealm[i] == '\\' && arealm[i + 1] != '\0') i++; if (arealm[i] != realm[j]) break; } } else { if (strcmp(arealm, realm) == 0) return auth; } } return NULL; }
/** Calculate extra space required by sip_transport_dup() */ isize_t sip_transport_xtra(char const *transport) { if (transport == sip_transport_udp || transport == sip_transport_tcp || transport == sip_transport_sctp || transport == sip_transport_ws || transport == sip_transport_wss || transport == sip_transport_tls || su_casematch(transport, sip_transport_udp) || su_casematch(transport, sip_transport_tcp) || su_casematch(transport, sip_transport_sctp) || su_casematch(transport, sip_transport_ws) || su_casematch(transport, sip_transport_wss) || su_casematch(transport, sip_transport_tls)) return 0; return MSG_STRING_SIZE(transport); }
/** Remove a named attribute from a list of attributes. */ sdp_attribute_t *sdp_attribute_remove(sdp_attribute_t **list, char const *name) { sdp_attribute_t *a; assert(list); if (list == NULL) return NULL; if (name == NULL) return NULL; for (a = *list; a; list = &a->a_next, a = *list) { if (su_casematch(name, a->a_name)) break; } if (a) { *list = a->a_next; a->a_next = NULL; } return a; }
/** Decode transport */ issize_t sip_transport_d(char **ss, char const **ttransport) { char const *transport; char *pn, *pv, *pt; size_t pn_len, pv_len, pt_len; char *s = *ss; #define TRANSPORT_MATCH(t) \ (su_casenmatch(s + 7, t + 7, (sizeof t) - 8) && \ (!s[sizeof(t) - 1] || IS_LWS(s[sizeof(t) - 1])) \ && (transport = t, s += sizeof(t) - 1)) if (!su_casenmatch(s, "SIP/2.0", 7) || (!TRANSPORT_MATCH(sip_transport_udp) && !TRANSPORT_MATCH(sip_transport_tcp) && !TRANSPORT_MATCH(sip_transport_sctp) && !TRANSPORT_MATCH(sip_transport_ws) && !TRANSPORT_MATCH(sip_transport_wss) && !TRANSPORT_MATCH(sip_transport_tls))) { /* Protocol name */ transport = pn = s; skip_token(&s); pn_len = s - pn; skip_lws(&s); if (pn_len == 0 || *s++ != '/') return -1; skip_lws(&s); /* Protocol version */ pv = s; skip_token(&s); pv_len = s - pv; skip_lws(&s); if (pv_len == 0 || *s++ != '/') return -1; skip_lws(&s); /* Transport protocol */ pt = s; skip_token(&s); pt_len = s - pt; if (pt_len == 0) return -1; /* Remove whitespace between protocol name and version */ if (pn + pn_len + 1 != pv) { pn[pn_len] = '/'; pv = memmove(pn + pn_len + 1, pv, pv_len); } /* Remove whitespace between protocol version and transport */ if (pv + pv_len + 1 != pt) { pv[pv_len] = '/'; pt = memmove(pv + pv_len + 1, pt, pt_len); pt[pt_len] = '\0'; /* extra whitespace? */ if (su_casematch(transport, sip_transport_udp)) transport = sip_transport_udp; else if (su_casematch(transport, sip_transport_tcp)) transport = sip_transport_tcp; else if (su_casematch(transport, sip_transport_sctp)) transport = sip_transport_sctp; else if (su_casematch(transport, sip_transport_ws)) transport = sip_transport_ws; else if (su_casematch(transport, sip_transport_wss)) transport = sip_transport_wss; else if (su_casematch(transport, sip_transport_tls)) transport = sip_transport_tls; } } if (IS_LWS(*s)) { *s++ = '\0'; skip_lws(&s); } *ss = s; *ttransport = transport; return 0; }
/** Find a Digest credential header with matching realm and opaque. */ msg_auth_t *auth_digest_credentials(msg_auth_t *auth, char const *realm, char const *opaque) { char const *arealm, *aopaque; for (; auth; auth = auth->au_next) { if (!su_casematch(auth->au_scheme, "Digest")) continue; if (realm) { int cmp = 1; arealm = msg_header_find_param(auth->au_common, "realm="); if (!arealm) continue; if (arealm[0] == '"') { /* Compare quoted arealm with unquoted realm */ int i, j; for (i = 1, j = 0, cmp = 1; arealm[i] != 0; i++, j++) { if (arealm[i] == '"' && realm[j] == 0) { cmp = 0; break; } if (arealm[i] == '\\' && arealm[i + 1] != '\0') i++; if (arealm[i] != realm[j]) break; } } else { cmp = strcmp(arealm, realm); } if (cmp) continue; } if (opaque) { int cmp = 1; aopaque = msg_header_find_param(auth->au_common, "opaque="); if (!aopaque) continue; if (aopaque[0] == '"') { /* Compare quoted aopaque with unquoted opaque */ int i, j; for (i = 1, j = 0, cmp = 1; aopaque[i] != 0; i++, j++) { if (aopaque[i] == '"' && opaque[j] == 0) { cmp = 0; break; } if (aopaque[i] == '\\' && aopaque[i + 1] != '\0') i++; if (aopaque[i] != opaque[j]) break; } } else { cmp = strcmp(aopaque, opaque); } if (cmp) continue; } return auth; } return NULL; }
/** Verify digest authentication */ void auth_check_digest(auth_mod_t *am, auth_status_t *as, auth_response_t *ar, auth_challenger_t const *ach) { char const *a1; auth_hexmd5_t a1buf, response; auth_passwd_t *apw; char const *phrase; msg_time_t now = msg_now(); if (am == NULL || as == NULL || ar == NULL || ach == NULL) { if (as) { as->as_status = 500, as->as_phrase = "Internal Server Error"; as->as_response = NULL; } return; } phrase = "Bad authorization"; #define PA "Authorization missing " if ((!ar->ar_username && (phrase = PA "username")) || (!ar->ar_nonce && (phrase = PA "nonce")) || (!ar->ar_uri && (phrase = PA "URI")) || (!ar->ar_response && (phrase = PA "response")) || /* (!ar->ar_opaque && (phrase = PA "opaque")) || */ /* Check for qop */ (ar->ar_qop && ((ar->ar_auth && !su_casematch(ar->ar_qop, "auth") && !su_casematch(ar->ar_qop, "\"auth\"")) || (ar->ar_auth_int && !su_casematch(ar->ar_qop, "auth-int") && !su_casematch(ar->ar_qop, "\"auth-int\""))) && (phrase = PA "has invalid qop"))) { assert(phrase); SU_DEBUG_5(("auth_method_digest: 400 %s\n", phrase)); as->as_status = 400, as->as_phrase = phrase; as->as_response = NULL; return; } if (as->as_nonce_issued == 0 /* Already validated nonce */ && auth_validate_digest_nonce(am, as, ar, now) < 0) { as->as_blacklist = am->am_blacklist; auth_challenge_digest(am, as, ach); return; } if (as->as_stale) { auth_challenge_digest(am, as, ach); return; } apw = auth_mod_getpass(am, ar->ar_username, ar->ar_realm); if (apw && apw->apw_hash) a1 = apw->apw_hash; else if (apw && apw->apw_pass) auth_digest_a1(ar, a1buf, apw->apw_pass), a1 = a1buf; else auth_digest_a1(ar, a1buf, "xyzzy"), a1 = a1buf, apw = NULL; if (ar->ar_md5sess) auth_digest_a1sess(ar, a1buf, a1), a1 = a1buf; auth_digest_response(ar, response, a1, as->as_method, as->as_body, as->as_bodylen); if (!apw || strcmp(response, ar->ar_response)) { if (am->am_forbidden) { as->as_status = 403, as->as_phrase = "Forbidden"; as->as_response = NULL; as->as_blacklist = am->am_blacklist; } else { auth_challenge_digest(am, as, ach); as->as_blacklist = am->am_blacklist; } SU_DEBUG_5(("auth_method_digest: response did not match\n")); return; } assert(apw); as->as_user = apw->apw_user; as->as_anonymous = apw == am->am_anon_user; as->as_ident = apw->apw_ident; if (am->am_nextnonce || am->am_mutual) auth_info_digest(am, as, ach); if (am->am_challenge) auth_challenge_digest(am, as, ach); SU_DEBUG_7(("auth_method_digest: successful authentication\n")); as->as_status = 0; /* Successful authentication! */ as->as_phrase = ""; }
/** Extract the HTTP message body, including separator line. * * @retval -1 error * @retval 0 cannot proceed * @retval other number of bytes extracted */ issize_t http_extract_body(msg_t *msg, http_t *http, char b[], isize_t bsiz, int eos) { issize_t m = 0; size_t body_len; int flags = http->http_flags; if (eos && bsiz == 0) { msg_mark_as_complete(msg, MSG_FLG_COMPLETE); return 0; } if (flags & MSG_FLG_TRAILERS) { /* The empty line after trailers */ if (!eos && (bsiz == 0 || (bsiz == 1 && b[0] == '\r'))) return 0; m = CRLF_TEST(b); assert(m > 0 || eos); /* We should be looking at an empty line */ /* We have completed trailers */ msg_mark_as_complete(msg, MSG_FLG_COMPLETE); return m; } if (flags & MSG_FLG_CHUNKS) return http_extract_chunk(msg, http, b, bsiz, eos); if (!(flags & MSG_FLG_BODY)) { /* We are looking at a potential empty line */ m = msg_extract_separator(msg, http, b, bsiz, eos); if (m == 0) /* Not yet */ return 0; http->http_flags |= MSG_FLG_BODY; b += m, bsiz -= m; } /* body_len is determined by rules in RFC2616 sections 4.3 and 4.4 */ /* 1XX, 204, 304 do not have message-body, ever */ if (http->http_status) { int status = http->http_status->st_status; if (status < 200 || status == 204 || status == 304) flags |= HTTP_FLG_NO_BODY; } if (flags & HTTP_FLG_NO_BODY) { msg_mark_as_complete(msg, MSG_FLG_COMPLETE); return m; } if (http->http_transfer_encoding) { if (/* NOTE - there is really no Transfer-Encoding: identity in RFC 2616 * but it was used in drafts... */ http->http_transfer_encoding->k_items && http->http_transfer_encoding->k_items[0] && !su_casematch(http->http_transfer_encoding->k_items[0], "identity")) { http->http_flags |= MSG_FLG_CHUNKS; if (http->http_flags & MSG_FLG_STREAMING) msg_set_streaming(msg, msg_start_streaming); if (m) return m; return http_extract_chunk(msg, http, b, bsiz, eos); } } if (http->http_content_length) body_len = http->http_content_length->l_length; /* We cannot parse multipart/byteranges ... */ else if (http->http_content_type && http->http_content_type->c_type && su_casematch(http->http_content_type->c_type, "multipart/byteranges")) return -1; else if (MSG_IS_MAILBOX(flags)) /* message fragments */ body_len = 0; else if (http->http_request) body_len = 0; else if (eos) body_len = bsiz; else return 0; /* XXX */ if (body_len == 0) { msg_mark_as_complete(msg, MSG_FLG_COMPLETE); return m; } if (http->http_flags & MSG_FLG_STREAMING) msg_set_streaming(msg, msg_start_streaming); if (m) return m; m = msg_extract_payload(msg, http, NULL, body_len, b, bsiz, eos); if (m == -1) return -1; /* We have now all message fragments in place */ http->http_flags |= MSG_FLG_FRAGS; if (bsiz >= body_len) { msg_mark_as_complete(msg, MSG_FLG_COMPLETE); } return m; }
/**@internal * * Detect NAT. * * Based on "received" and possible "rport" parameters in the top-most Via, * check and update our NAT status. * * @retval ob_nat_changed (2) change in public NAT binding detected * @retval ob_nat_detected (1) NAT binding detected * @retval ob_no_nat (0) no NAT binding detected * @retval -1 an error occurred */ static int outbound_nat_detect(outbound_t *ob, sip_t const *request, sip_t const *response) { sip_via_t const *v; int one = 1; char const *received, *rport; char *nat_detected, *nat_port; char *new_detected, *new_port; assert(request && request->sip_request); assert(response && response->sip_status); if (!ob || !response || !response->sip_via || !request->sip_via) return -1; v = response->sip_via; received = v->v_received; if (!received || !strcmp(received, request->sip_via->v_host)) return 0; if (!host_is_ip_address(received)) { if (received[0]) SU_DEBUG_3(("outbound(%p): Via with invalid received=%s\n", (void *)ob->ob_owner, received)); return 0; } rport = sip_via_port(v, &one); assert(rport); nat_detected = ob->ob_nat_detected; nat_port = ob->ob_nat_port; if (nat_detected && host_cmp(received, nat_detected) == 0) { if (nat_port && su_casematch(rport, nat_port)) return 1; if (!v->v_rport || !v->v_rport[0]) return 1; } if (!nat_detected) { SU_DEBUG_5(("outbound(%p): detected NAT: %s != %s\n", (void *)ob->ob_owner, v->v_host, received)); if (ob->ob_oo && ob->ob_oo->oo_status) ob->ob_oo->oo_status(ob->ob_owner, ob, 101, "NAT detected", TAG_END()); } else { SU_DEBUG_5(("outbound(%p): NAT binding changed: " "[%s]:%s != [%s]:%s\n", (void *)ob->ob_owner, nat_detected, nat_port, received, rport)); if (ob->ob_oo && ob->ob_oo->oo_status) ob->ob_oo->oo_status(ob->ob_owner, ob, 102, "NAT binding changed", TAG_END()); } /* Save our nat binding */ new_detected = su_strdup(ob->ob_home, received); new_port = su_strdup(ob->ob_home, rport); if (!new_detected || !new_port) { su_free(ob->ob_home, new_detected); su_free(ob->ob_home, new_port); return -1; } ob->ob_nat_detected = new_detected; ob->ob_nat_port = new_port; su_free(ob->ob_home, nat_detected); su_free(ob->ob_home, nat_port); return 2; }
/** Count or get matching records from cache */ static int sres_cache_get0(sres_htable_t *htable, sres_rr_hash_entry_t **iter, uint16_t type, char const *domain, time_t now, sres_record_t **cached, int len, struct frame *previous) { sres_cname_record_t *cname = NULL; int dcount = 0, derrorcount = 0, ccount = 0; for (; iter && *iter; iter = sres_htable_next(htable, iter)) { sres_record_t *rr = (*iter)->rr; if (rr == NULL) continue; if (now > (*iter)->rr_expires) continue; if (rr->sr_name == NULL) continue; if (!su_casematch(rr->sr_name, domain)) continue; if (rr->sr_type == type || type == sres_qtype_any) { if (rr->sr_status == SRES_RECORD_ERR && type == sres_qtype_any) continue; if (cached) { if (dcount >= len) return -1; cached[dcount] = rr, rr->sr_refcount++; } dcount++; if (rr->sr_status) derrorcount++; } if (type != sres_type_cname && rr->sr_type == sres_type_cname) { if (rr->sr_status == 0) cname = rr->sr_cname; } } if (cname && dcount == derrorcount) { /* Nothing found, trace CNAMEs */ unsigned hash; struct frame *f, frame; frame.previous = previous; frame.domain = domain; hash = sres_hash_key(domain = cname->cn_cname); /* Check for cname loops */ for (f = previous; f; f = f->previous) { if (su_casematch(domain, f->domain)) break; } if (f == NULL) { ccount = sres_cache_get0(htable, sres_htable_hash(htable, hash), type, domain, now, cached ? cached + dcount : NULL, cached ? len - dcount : 0, &frame); } if (ccount < 0) return ccount; } return dcount + ccount; }
su_inline int tls_post_connection_check(tport_t *self, tls_t *tls) { X509 *cert; int extcount; int i, j, error; if (!tls) return -1; cert = SSL_get_peer_certificate(tls->con); if (!cert) { SU_DEBUG_7(("%s(%p): Peer did not provide X.509 Certificate.\n", __func__, (void *) self)); if (self->tp_accepted && tls->verify_incoming) return X509_V_ERR_CERT_UNTRUSTED; else if (!self->tp_accepted && tls->verify_outgoing) return X509_V_ERR_CERT_UNTRUSTED; else return X509_V_OK; } tls->subjects = su_strlst_create(tls->home); if (!tls->subjects) return X509_V_ERR_OUT_OF_MEM; extcount = X509_get_ext_count(cert); /* Find matching subjectAltName.DNS */ for (i = 0; i < extcount; i++) { X509_EXTENSION *ext; char const *name; #if OPENSSL_VERSION_NUMBER > 0x10000000L const X509V3_EXT_METHOD *vp; #else X509V3_EXT_METHOD *vp; #endif STACK_OF(CONF_VALUE) *values; CONF_VALUE *value; void *d2i; ext = X509_get_ext(cert, i); name = OBJ_nid2sn(OBJ_obj2nid(X509_EXTENSION_get_object(ext))); if (strcmp(name, "subjectAltName") != 0) continue; vp = X509V3_EXT_get(ext); if (!vp) continue; d2i = X509V3_EXT_d2i(ext); values = vp->i2v(vp, d2i, NULL); for (j = 0; j < sk_CONF_VALUE_num(values); j++) { value = sk_CONF_VALUE_value(values, j); if (strcmp(value->name, "DNS") == 0) su_strlst_dup_append(tls->subjects, value->value); if (strcmp(value->name, "IP") == 0) su_strlst_dup_append(tls->subjects, value->value); else if (strcmp(value->name, "URI") == 0) su_strlst_dup_append(tls->subjects, value->value); } } { X509_NAME *subject; char name[256]; subject = X509_get_subject_name(cert); if (subject) { if (X509_NAME_get_text_by_NID(subject, NID_commonName, name, sizeof name) > 0) { usize_t k, N = su_strlst_len(tls->subjects); name[(sizeof name) - 1] = '\0'; for (k = 0; k < N; k++) if (su_casematch(su_strlst_item(tls->subjects, k), name) == 0) break; if (k >= N) su_strlst_dup_append(tls->subjects, name); } } } X509_free(cert); error = SSL_get_verify_result(tls->con); if (cert && error == X509_V_OK) tls->x509_verified = 1; if (tport_log->log_level >= 7) { int i, len = su_strlst_len(tls->subjects); for (i=0; i < len; i++) SU_DEBUG_7(("%s(%p): Peer Certificate Subject %i: %s\n", \ __func__, (void *)self, i, su_strlst_item(tls->subjects, i))); if (i == 0) SU_DEBUG_7(("%s(%p): Peer Certificate provided no usable subjects.\n", __func__, (void *)self)); } /* Verify incoming connections */ if (self->tp_accepted) { if (!tls->verify_incoming) return X509_V_OK; if (!tls->x509_verified) return error; if (tls->verify_subj_in) { su_strlst_t const *subjects = self->tp_pri->pri_primary->tp_subjects; int i, items; items = subjects ? su_strlst_len(subjects) : 0; if (items == 0) return X509_V_OK; for (i=0; i < items; i++) { if (tport_subject_search(su_strlst_item(subjects, i), tls->subjects)) return X509_V_OK; } SU_DEBUG_3(("%s(%p): Peer Subject Mismatch (incoming connection)\n", \ __func__, (void *)self)); return X509_V_ERR_CERT_UNTRUSTED; } } /* Verify outgoing connections */ else { char const *subject = self->tp_canon; if (!tls->verify_outgoing) return X509_V_OK; if (!tls->x509_verified || !subject) return error; if (tls->verify_subj_out) { if (tport_subject_search(subject, tls->subjects)) return X509_V_OK; /* Subject match found in verified certificate chain */ SU_DEBUG_3(("%s(%p): Peer Subject Mismatch (%s)\n", \ __func__, (void *)self, subject)); return X509_V_ERR_CERT_UNTRUSTED; } } return error; }