res_pjsip_outbound_authenticator_digest.c 7.2 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234
  1. /*
  2. * Asterisk -- An open source telephony toolkit.
  3. *
  4. * Copyright (C) 2013, Digium, Inc.
  5. *
  6. * Mark Michelson <mmichelson@digium.com>
  7. *
  8. * See http://www.asterisk.org for more information about
  9. * the Asterisk project. Please do not directly contact
  10. * any of the maintainers of this project for assistance;
  11. * the project provides a web site, mailing lists and IRC
  12. * channels for your use.
  13. *
  14. * This program is free software, distributed under the terms of
  15. * the GNU General Public License Version 2. See the LICENSE file
  16. * at the top of the source tree.
  17. */
  18. /*** MODULEINFO
  19. <depend>pjproject</depend>
  20. <depend>res_pjsip</depend>
  21. <support_level>core</support_level>
  22. ***/
  23. #include "asterisk.h"
  24. #include <pjsip.h>
  25. #include "asterisk/res_pjsip.h"
  26. #include "asterisk/logger.h"
  27. #include "asterisk/module.h"
  28. #include "asterisk/strings.h"
  29. static pjsip_www_authenticate_hdr *get_auth_header(pjsip_rx_data *challenge,
  30. const void *start)
  31. {
  32. pjsip_hdr_e search_type;
  33. if (challenge->msg_info.msg->line.status.code == PJSIP_SC_UNAUTHORIZED) {
  34. search_type = PJSIP_H_WWW_AUTHENTICATE;
  35. } else if (challenge->msg_info.msg->line.status.code == PJSIP_SC_PROXY_AUTHENTICATION_REQUIRED) {
  36. search_type = PJSIP_H_PROXY_AUTHENTICATE;
  37. } else {
  38. ast_log(LOG_ERROR,
  39. "Status code %d was received when it should have been 401 or 407.\n",
  40. challenge->msg_info.msg->line.status.code);
  41. return NULL ;
  42. }
  43. return pjsip_msg_find_hdr(challenge->msg_info.msg, search_type, start);
  44. }
  45. static int set_outbound_authentication_credentials(pjsip_auth_clt_sess *auth_sess,
  46. const struct ast_sip_auth_vector *auth_vector, pjsip_rx_data *challenge,
  47. pjsip_www_authenticate_hdr *auth_hdr)
  48. {
  49. size_t auth_size = AST_VECTOR_SIZE(auth_vector);
  50. struct ast_sip_auth **auths = ast_alloca(auth_size * sizeof(*auths));
  51. pjsip_cred_info *auth_creds = ast_alloca(auth_size * sizeof(*auth_creds));
  52. int res = 0;
  53. int i;
  54. if (ast_sip_retrieve_auths(auth_vector, auths)) {
  55. res = -1;
  56. goto cleanup;
  57. }
  58. for (i = 0; i < auth_size; ++i) {
  59. if (ast_strlen_zero(auths[i]->realm)) {
  60. auth_creds[i].realm = auth_hdr->challenge.common.realm;
  61. } else {
  62. pj_cstr(&auth_creds[i].realm, auths[i]->realm);
  63. }
  64. pj_cstr(&auth_creds[i].username, auths[i]->auth_user);
  65. pj_cstr(&auth_creds[i].scheme, "digest");
  66. switch (auths[i]->type) {
  67. case AST_SIP_AUTH_TYPE_USER_PASS:
  68. pj_cstr(&auth_creds[i].data, auths[i]->auth_pass);
  69. auth_creds[i].data_type = PJSIP_CRED_DATA_PLAIN_PASSWD;
  70. break;
  71. case AST_SIP_AUTH_TYPE_MD5:
  72. pj_cstr(&auth_creds[i].data, auths[i]->md5_creds);
  73. auth_creds[i].data_type = PJSIP_CRED_DATA_DIGEST;
  74. break;
  75. case AST_SIP_AUTH_TYPE_ARTIFICIAL:
  76. ast_log(LOG_ERROR, "Trying to set artificial outbound auth credentials shouldn't happen.\n");
  77. break;
  78. }
  79. }
  80. pjsip_auth_clt_set_credentials(auth_sess, auth_size, auth_creds);
  81. cleanup:
  82. ast_sip_cleanup_auths(auths, auth_size);
  83. return res;
  84. }
  85. static int digest_create_request_with_auth_from_old(const struct ast_sip_auth_vector *auths,
  86. pjsip_rx_data *challenge, pjsip_tx_data *old_request, pjsip_tx_data **new_request)
  87. {
  88. pjsip_auth_clt_sess auth_sess;
  89. pjsip_cseq_hdr *cseq;
  90. pj_status_t status;
  91. struct ast_sip_endpoint *endpoint;
  92. char *id = NULL;
  93. const char *id_type;
  94. pjsip_www_authenticate_hdr *auth_hdr;
  95. struct ast_str *realms;
  96. pjsip_dialog *dlg;
  97. dlg = pjsip_rdata_get_dlg(challenge);
  98. if (dlg) {
  99. endpoint = ast_sip_dialog_get_endpoint(dlg);
  100. id = endpoint ? ast_strdupa(ast_sorcery_object_get_id(endpoint)) : NULL;
  101. ao2_cleanup(endpoint);
  102. id_type = "Endpoint";
  103. }
  104. /* If there was no dialog, then this is probably a REGISTER so no endpoint */
  105. if (!id) {
  106. id = ast_alloca(AST_SOCKADDR_BUFLEN);
  107. pj_sockaddr_print(&challenge->pkt_info.src_addr, id, AST_SOCKADDR_BUFLEN, 3);
  108. id_type = "Host";
  109. }
  110. auth_hdr = get_auth_header(challenge, NULL);
  111. if (auth_hdr == NULL) {
  112. ast_log(LOG_ERROR, "%s: '%s': Unable to find authenticate header in challenge.\n",
  113. id_type, id);
  114. return -1;
  115. }
  116. if (pjsip_auth_clt_init(&auth_sess, ast_sip_get_pjsip_endpoint(),
  117. old_request->pool, 0) != PJ_SUCCESS) {
  118. ast_log(LOG_ERROR, "%s: '%s': Failed to initialize client authentication session\n",
  119. id_type, id);
  120. return -1;
  121. }
  122. if (set_outbound_authentication_credentials(&auth_sess, auths, challenge, auth_hdr)) {
  123. ast_log(LOG_WARNING, "%s: '%s': Failed to set authentication credentials\n",
  124. id_type, id);
  125. #if defined(HAVE_PJSIP_AUTH_CLT_DEINIT)
  126. /* In case it is not a noop here in the future. */
  127. pjsip_auth_clt_deinit(&auth_sess);
  128. #endif
  129. return -1;
  130. }
  131. status = pjsip_auth_clt_reinit_req(&auth_sess, challenge, old_request, new_request);
  132. #if defined(HAVE_PJSIP_AUTH_CLT_DEINIT)
  133. /* Release any cached auths */
  134. pjsip_auth_clt_deinit(&auth_sess);
  135. #endif
  136. switch (status) {
  137. case PJ_SUCCESS:
  138. /* PJSIP creates a new transaction for new_request (meaning it creates a new
  139. * branch). However, it recycles the Call-ID, from-tag, and CSeq from the
  140. * original request. Some SIP implementations will not process the new request
  141. * since the CSeq is the same as the original request. Incrementing it here
  142. * fixes the interop issue
  143. */
  144. cseq = pjsip_msg_find_hdr((*new_request)->msg, PJSIP_H_CSEQ, NULL);
  145. ast_assert(cseq != NULL);
  146. ++cseq->cseq;
  147. return 0;
  148. case PJSIP_ENOCREDENTIAL:
  149. realms = ast_str_create(32);
  150. if (realms) {
  151. ast_str_append(&realms, 0, "%.*s", (int)auth_hdr->challenge.common.realm.slen,
  152. auth_hdr->challenge.common.realm.ptr);
  153. while((auth_hdr = get_auth_header(challenge, auth_hdr->next))) {
  154. ast_str_append(&realms, 0, ",%.*s", (int)auth_hdr->challenge.common.realm.slen,
  155. auth_hdr->challenge.common.realm.ptr);
  156. }
  157. }
  158. ast_log(LOG_WARNING,
  159. "%s: '%s': Unable to create request with auth. "
  160. "No auth credentials for realm(s) '%s' in challenge.\n", id_type, id,
  161. realms ? ast_str_buffer(realms) : "<unknown>");
  162. ast_free(realms);
  163. break;
  164. case PJSIP_EAUTHSTALECOUNT:
  165. ast_log(LOG_WARNING,
  166. "%s: '%s': Unable to create request with auth. Number of stale retries exceeded.\n",
  167. id_type, id);
  168. break;
  169. case PJSIP_EFAILEDCREDENTIAL:
  170. ast_log(LOG_WARNING, "%s: '%s': Authentication credentials not accepted by server.\n",
  171. id_type, id);
  172. break;
  173. default:
  174. ast_log(LOG_WARNING, "%s: '%s': Unable to create request with auth. Unknown failure.\n",
  175. id_type, id);
  176. break;
  177. }
  178. return -1;
  179. }
  180. static int digest_create_request_with_auth(const struct ast_sip_auth_vector *auths, pjsip_rx_data *challenge,
  181. pjsip_transaction *tsx, pjsip_tx_data **new_request)
  182. {
  183. return digest_create_request_with_auth_from_old(auths, challenge, tsx->last_tx, new_request);
  184. }
  185. static struct ast_sip_outbound_authenticator digest_authenticator = {
  186. .create_request_with_auth = digest_create_request_with_auth,
  187. .create_request_with_auth_from_old = digest_create_request_with_auth_from_old,
  188. };
  189. static int load_module(void)
  190. {
  191. CHECK_PJSIP_MODULE_LOADED();
  192. if (ast_sip_register_outbound_authenticator(&digest_authenticator)) {
  193. return AST_MODULE_LOAD_DECLINE;
  194. }
  195. return AST_MODULE_LOAD_SUCCESS;
  196. }
  197. static int unload_module(void)
  198. {
  199. ast_sip_unregister_outbound_authenticator(&digest_authenticator);
  200. return 0;
  201. }
  202. AST_MODULE_INFO(ASTERISK_GPL_KEY, AST_MODFLAG_LOAD_ORDER, "PJSIP authentication resource",
  203. .support_level = AST_MODULE_SUPPORT_CORE,
  204. .load = load_module,
  205. .unload = unload_module,
  206. .load_pri = AST_MODPRI_CHANNEL_DEPEND,
  207. );