res_pjsip_outbound_authenticator_digest.c 4.9 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169
  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. pjsip_hdr_e search_type;
  31. if (challenge->msg_info.msg->line.status.code == PJSIP_SC_UNAUTHORIZED) {
  32. search_type = PJSIP_H_WWW_AUTHENTICATE;
  33. } else if (challenge->msg_info.msg->line.status.code == PJSIP_SC_PROXY_AUTHENTICATION_REQUIRED) {
  34. search_type = PJSIP_H_PROXY_AUTHENTICATE;
  35. } else {
  36. ast_log(LOG_ERROR,
  37. "Status code %d was received when it should have been 401 or 407.\n",
  38. challenge->msg_info.msg->line.status.code);
  39. return NULL ;
  40. }
  41. return pjsip_msg_find_hdr(challenge->msg_info.msg, search_type, NULL);
  42. }
  43. static int set_outbound_authentication_credentials(pjsip_auth_clt_sess *auth_sess,
  44. const struct ast_sip_auth_vector *auth_vector, pjsip_rx_data *challenge)
  45. {
  46. size_t auth_size = AST_VECTOR_SIZE(auth_vector);
  47. struct ast_sip_auth **auths = ast_alloca(auth_size * sizeof(*auths));
  48. pjsip_cred_info *auth_creds = ast_alloca(auth_size * sizeof(*auth_creds));
  49. pjsip_www_authenticate_hdr *auth_hdr = NULL;
  50. int res = 0;
  51. int i;
  52. if (ast_sip_retrieve_auths(auth_vector, auths)) {
  53. res = -1;
  54. goto cleanup;
  55. }
  56. auth_hdr = get_auth_header(challenge);
  57. if (auth_hdr == NULL) {
  58. res = -1;
  59. ast_log(LOG_ERROR, "Unable to find authenticate header in challenge.\n");
  60. goto cleanup;
  61. }
  62. for (i = 0; i < auth_size; ++i) {
  63. if (ast_strlen_zero(auths[i]->realm)) {
  64. auth_creds[i].realm = auth_hdr->challenge.common.realm;
  65. } else {
  66. pj_cstr(&auth_creds[i].realm, auths[i]->realm);
  67. }
  68. pj_cstr(&auth_creds[i].username, auths[i]->auth_user);
  69. pj_cstr(&auth_creds[i].scheme, "digest");
  70. switch (auths[i]->type) {
  71. case AST_SIP_AUTH_TYPE_USER_PASS:
  72. pj_cstr(&auth_creds[i].data, auths[i]->auth_pass);
  73. auth_creds[i].data_type = PJSIP_CRED_DATA_PLAIN_PASSWD;
  74. break;
  75. case AST_SIP_AUTH_TYPE_MD5:
  76. pj_cstr(&auth_creds[i].data, auths[i]->md5_creds);
  77. auth_creds[i].data_type = PJSIP_CRED_DATA_DIGEST;
  78. break;
  79. case AST_SIP_AUTH_TYPE_ARTIFICIAL:
  80. ast_log(LOG_ERROR, "Trying to set artificial outbound auth credentials shouldn't happen.\n");
  81. break;
  82. }
  83. }
  84. pjsip_auth_clt_set_credentials(auth_sess, auth_size, auth_creds);
  85. cleanup:
  86. ast_sip_cleanup_auths(auths, auth_size);
  87. return res;
  88. }
  89. static int digest_create_request_with_auth(const struct ast_sip_auth_vector *auths, pjsip_rx_data *challenge,
  90. pjsip_transaction *tsx, pjsip_tx_data **new_request)
  91. {
  92. pjsip_auth_clt_sess auth_sess;
  93. if (pjsip_auth_clt_init(&auth_sess, ast_sip_get_pjsip_endpoint(),
  94. tsx->pool, 0) != PJ_SUCCESS) {
  95. ast_log(LOG_WARNING, "Failed to initialize client authentication session\n");
  96. return -1;
  97. }
  98. if (set_outbound_authentication_credentials(&auth_sess, auths, challenge)) {
  99. ast_log(LOG_WARNING, "Failed to set authentication credentials\n");
  100. return -1;
  101. }
  102. switch (pjsip_auth_clt_reinit_req(&auth_sess, challenge,
  103. tsx->last_tx, new_request)) {
  104. case PJ_SUCCESS:
  105. return 0;
  106. case PJSIP_ENOCREDENTIAL:
  107. ast_log(LOG_WARNING, "Unable to create request with auth."
  108. "No auth credentials for any realms in challenge.\n");
  109. break;
  110. case PJSIP_EAUTHSTALECOUNT:
  111. ast_log(LOG_WARNING, "Unable to create request with auth."
  112. "Number of stale retries exceeded\n");
  113. break;
  114. case PJSIP_EFAILEDCREDENTIAL:
  115. ast_log(LOG_WARNING, "Authentication credentials not accepted by server\n");
  116. break;
  117. default:
  118. ast_log(LOG_WARNING, "Unable to create request with auth. Unknown failure\n");
  119. break;
  120. }
  121. return -1;
  122. }
  123. static struct ast_sip_outbound_authenticator digest_authenticator = {
  124. .create_request_with_auth = digest_create_request_with_auth,
  125. };
  126. static int load_module(void)
  127. {
  128. CHECK_PJSIP_MODULE_LOADED();
  129. if (ast_sip_register_outbound_authenticator(&digest_authenticator)) {
  130. return AST_MODULE_LOAD_DECLINE;
  131. }
  132. return AST_MODULE_LOAD_SUCCESS;
  133. }
  134. static int unload_module(void)
  135. {
  136. ast_sip_unregister_outbound_authenticator(&digest_authenticator);
  137. return 0;
  138. }
  139. AST_MODULE_INFO(ASTERISK_GPL_KEY, AST_MODFLAG_LOAD_ORDER, "PJSIP authentication resource",
  140. .support_level = AST_MODULE_SUPPORT_CORE,
  141. .load = load_module,
  142. .unload = unload_module,
  143. .load_pri = AST_MODPRI_CHANNEL_DEPEND,
  144. );