From b1e8c2f007e533c7a01c3ced2a34620f4737ca18 Mon Sep 17 00:00:00 2001 From: Federico Cabiddu Date: Fri, 16 Sep 2016 10:11:44 +0200 Subject: [PATCH] http_async_client: added support for authentication --- modules/http_async_client/async_http.c | 92 ++++++++++++++++-- modules/http_async_client/async_http.h | 15 +++ .../doc/http_async_client_admin.xml | 19 ++++ modules/http_async_client/hm_hash.h | 4 + .../http_async_client/http_async_client_mod.c | 95 ++++++++++++++++--- modules/http_async_client/http_multi.c | 12 +++ 6 files changed, 219 insertions(+), 18 deletions(-) diff --git a/modules/http_async_client/async_http.c b/modules/http_async_client/async_http.c index 67915f10df5..d3ba6ad7bf3 100644 --- a/modules/http_async_client/async_http.c +++ b/modules/http_async_client/async_http.c @@ -213,7 +213,7 @@ void notification_socket_cb(int fd, short event, void *arg) const async_http_worker_t *worker = (async_http_worker_t *) arg; int received; - int i; + int i, len; async_query_t *aq; http_m_params_t query_params; @@ -237,6 +237,8 @@ void notification_socket_cb(int fd, short event, void *arg) query_params.timeout = aq->query_params.timeout; query_params.tls_verify_peer = aq->query_params.tls_verify_peer; query_params.tls_verify_host = aq->query_params.tls_verify_host; + query_params.authmethod = aq->query_params.authmethod; + query_params.headers = NULL; for (i = 0 ; i < aq->query_params.headers.len ; i++) { query_params.headers = curl_slist_append(query_params.headers, aq->query_params.headers.t[i]); @@ -248,7 +250,7 @@ void notification_socket_cb(int fd, short event, void *arg) if (aq->query_params.tls_client_cert.s && aq->query_params.tls_client_cert.len > 0) { if (shm_str_dup(&query_params.tls_client_cert, &(aq->query_params.tls_client_cert)) < 0) { LM_ERR("Error allocating query_params.tls_client_cert\n"); - return; + goto done; } } @@ -257,7 +259,7 @@ void notification_socket_cb(int fd, short event, void *arg) if (aq->query_params.tls_client_key.s && aq->query_params.tls_client_key.len > 0) { if (shm_str_dup(&query_params.tls_client_key, &(aq->query_params.tls_client_key)) < 0) { LM_ERR("Error allocating query_params.tls_client_key\n"); - return; + goto done; } } @@ -266,7 +268,7 @@ void notification_socket_cb(int fd, short event, void *arg) if (aq->query_params.tls_ca_path.s && aq->query_params.tls_ca_path.len > 0) { if (shm_str_dup(&query_params.tls_ca_path, &(aq->query_params.tls_ca_path)) < 0) { LM_ERR("Error allocating query_params.tls_ca_path\n"); - return; + goto done; } } @@ -275,9 +277,35 @@ void notification_socket_cb(int fd, short event, void *arg) if (aq->query_params.body.s && aq->query_params.body.len > 0) { if (shm_str_dup(&query_params.body, &(aq->query_params.body)) < 0) { LM_ERR("Error allocating query_params.body\n"); - return; + goto done; } } + + if (aq->query_params.username) { + len = strlen(aq->query_params.username); + query_params.username = shm_malloc(len+1); + + if(query_params.username == NULL) { + LM_ERR("error in shm_malloc\n"); + goto done; + } + + strncpy(query_params.username, aq->query_params.username, len); + query_params.username[len] = '\0'; + } + + if (aq->query_params.password) { + len = strlen(aq->query_params.password); + query_params.password = shm_malloc(len+1); + + if(query_params.password == NULL) { + LM_ERR("error in shm_malloc\n"); + goto done; + } + + strncpy(query_params.password, aq->query_params.password, len); + query_params.password[len] = '\0'; + } LM_DBG("query received: [%.*s] (%p)\n", query.len, query.s, aq); @@ -286,6 +314,7 @@ void notification_socket_cb(int fd, short event, void *arg) free_async_query(aq); } +done: if (query_params.tls_client_cert.s && query_params.tls_client_cert.len > 0) { shm_free(query_params.tls_client_cert.s); query_params.tls_client_cert.s = NULL; @@ -307,6 +336,16 @@ void notification_socket_cb(int fd, short event, void *arg) query_params.body.len = 0; } + if (query_params.username) { + shm_free(query_params.username); + query_params.username = NULL; + } + + if (query_params.password) { + shm_free(query_params.password); + query_params.password = NULL; + } + return; } @@ -324,6 +363,7 @@ int async_send_query(sip_msg_t *msg, str *query, cfg_action_t *act) unsigned int tlabel = 0; short suspend = 0; int dsize; + int len; tm_cell_t *t = 0; if(query==0) { @@ -375,7 +415,8 @@ int async_send_query(sip_msg_t *msg, str *query, cfg_action_t *act) aq->query_params.timeout = ah_params.timeout; aq->query_params.headers = ah_params.headers; aq->query_params.method = ah_params.method; - + aq->query_params.authmethod = ah_params.authmethod; + q_idx++; snprintf(q_id, MAX_ID_LEN+1, "%u-%u", (unsigned int)getpid(), q_idx); strncpy(aq->id, q_id, strlen(q_id)); @@ -416,6 +457,34 @@ int async_send_query(sip_msg_t *msg, str *query, cfg_action_t *act) } } + aq->query_params.username = NULL; + if (ah_params.username) { + len = strlen(ah_params.username); + aq->query_params.username = shm_malloc(len+1); + + if(aq->query_params.username == NULL) { + LM_ERR("error in shm_malloc\n"); + goto error; + } + + strncpy(aq->query_params.username, ah_params.username, len); + aq->query_params.username[len] = '\0'; + } + + aq->query_params.password = NULL; + if (ah_params.password) { + len = strlen(ah_params.password); + aq->query_params.password = shm_malloc(len+1); + + if(aq->query_params.password == NULL) { + LM_ERR("error in shm_malloc\n"); + goto error; + } + + strncpy(aq->query_params.password, ah_params.password, len); + aq->query_params.password[len] = '\0'; + } + set_query_params(&ah_params); if(async_push_query(aq)<0) { @@ -473,6 +542,7 @@ void set_query_params(struct query_params *p) { p->suspend_transaction = 1; p->timeout = http_timeout; p->method = AH_METH_DEFAULT; + p->authmethod = default_authmethod; if (p->tls_client_cert.s && p->tls_client_cert.len > 0) { shm_free(p->tls_client_cert.s); @@ -515,6 +585,16 @@ void set_query_params(struct query_params *p) { p->body.s = NULL; p->body.len = 0; } + + if (p->username) { + shm_free(p->username); + p->username = NULL; + } + + if (p->password) { + shm_free(p->password); + p->password = NULL; + } } int header_list_add(struct header_list *hl, str* hdr) { diff --git a/modules/http_async_client/async_http.h b/modules/http_async_client/async_http.h index 862ee2b0ae0..856e5d0752c 100644 --- a/modules/http_async_client/async_http.h +++ b/modules/http_async_client/async_http.h @@ -59,6 +59,7 @@ extern str tls_client_cert; extern str tls_client_key; extern str tls_ca_path; +extern unsigned int default_authmethod; typedef struct async_http_worker { int notication_socket[2]; @@ -92,6 +93,10 @@ struct query_params { str tls_client_key; str tls_ca_path; str body; + + unsigned int authmethod; + char* username; + char* password; }; extern struct query_params ah_params; @@ -162,6 +167,16 @@ static inline void free_async_query(async_query_t *aq) aq->query_params.body.len = 0; } + if (aq->query_params.username) { + shm_free(aq->query_params.username); + aq->query_params.username = NULL; + } + + if (aq->query_params.password) { + shm_free(aq->query_params.password); + aq->query_params.password = NULL; + } + shm_free(aq); } diff --git a/modules/http_async_client/doc/http_async_client_admin.xml b/modules/http_async_client/doc/http_async_client_admin.xml index e5a83ea3286..9e9cd2b067a 100644 --- a/modules/http_async_client/doc/http_async_client_admin.xml +++ b/modules/http_async_client/doc/http_async_client_admin.xml @@ -589,6 +589,25 @@ xlog("L_INFO", "received reply for query $http_query_id\n"); tls_client_cert: sets the client certificate to use (see http_set_tls_client_cert()) tls_client_key: sets the client certificate key to use (see http_set_tls_client_key()) tls_ca_path: sets the CA certificate path to use (see http_set_tls_ca_path()) + authmethod: + Sets the preferred authentication mode for HTTP/HTTPS requests. The value is a bitmap + and multiple methods can be used. Note that in this case, the CURL library will make an + extra request to discover server-supported authentication methods. You may want to use + a specific value. + + Valid values are: + + 1 - BASIC authentication + 2 - HTTP Digest authentication + 4 - GSS-Negotiate authentication + 8 - NTLM authentication + 16 - HTTP Digest with IE flavour + + Default value is 3 - BASIC and Digest authentication. + + + username: sets the username to use for authenticated requests + password: sets the password to use for authenticated requests suspend: if set to 0 it doesn't suspend the current transaction before performing the query (see http_async_suspend()) diff --git a/modules/http_async_client/hm_hash.h b/modules/http_async_client/hm_hash.h index 6dd44ff75fd..162605bbbb4 100644 --- a/modules/http_async_client/hm_hash.h +++ b/modules/http_async_client/hm_hash.h @@ -74,6 +74,10 @@ typedef struct hm_params { str tls_client_key; str tls_ca_path; str body; + + unsigned int authmethod; + char* username; + char* password; } http_m_params_t; typedef struct http_m_cell diff --git a/modules/http_async_client/http_async_client_mod.c b/modules/http_async_client/http_async_client_mod.c index 93389cd355c..8bd60734fbd 100644 --- a/modules/http_async_client/http_async_client_mod.c +++ b/modules/http_async_client/http_async_client_mod.c @@ -75,6 +75,7 @@ str tls_client_key = STR_STATIC_INIT(""); // client SSL certificate key path, de str tls_ca_path = STR_STATIC_INIT(""); // certificate authority dir path, defaults to NULL static char *memory_manager = "shm"; extern int curl_memory_manager; +unsigned int default_authmethod = CURLAUTH_BASIC | CURLAUTH_DIGEST; static int mod_init(void); static int child_init(int); @@ -128,7 +129,8 @@ enum http_req_name_t { E_HRN_HDR, E_HRN_METHOD, E_HRN_TIMEOUT, E_HRN_TLS_CA_PATH, E_HRN_TLS_CLIENT_KEY, E_HRN_TLS_CLIENT_CERT, E_HRN_SUSPEND, - E_HRN_BODY + E_HRN_BODY, E_HRN_AUTHMETHOD, E_HRN_USERNAME, + E_HRN_PASSWORD }; static cmd_export_t cmds[]={ @@ -156,17 +158,18 @@ static cmd_export_t cmds[]={ }; static param_export_t params[]={ -{"workers", INT_PARAM, &num_workers}, - {"connection_timeout", INT_PARAM, &http_timeout}, - {"hash_size", INT_PARAM, &hash_size}, - {"tls_version", INT_PARAM, &tls_version}, - {"tls_verify_host", INT_PARAM, &tls_verify_host}, - {"tls_verify_peer", INT_PARAM, &tls_verify_peer}, - {"curl_verbose", INT_PARAM, &curl_verbose}, - {"tls_client_cert", PARAM_STR, &tls_client_cert}, - {"tls_client_key", PARAM_STR, &tls_client_key}, - {"tls_ca_path", PARAM_STR, &tls_ca_path}, - {"memory_manager", PARAM_STRING,&memory_manager}, + {"workers", INT_PARAM, &num_workers}, + {"connection_timeout", INT_PARAM, &http_timeout}, + {"hash_size", INT_PARAM, &hash_size}, + {"tls_version", INT_PARAM, &tls_version}, + {"tls_verify_host", INT_PARAM, &tls_verify_host}, + {"tls_verify_peer", INT_PARAM, &tls_verify_peer}, + {"curl_verbose", INT_PARAM, &curl_verbose}, + {"tls_client_cert", PARAM_STR, &tls_client_cert}, + {"tls_client_key", PARAM_STR, &tls_client_key}, + {"tls_ca_path", PARAM_STR, &tls_ca_path}, + {"memory_manager", PARAM_STRING, &memory_manager}, + {"authmethod", PARAM_INT, &default_authmethod }, {0, 0, 0} }; @@ -573,6 +576,33 @@ static int set_query_param(str* param, str input) return 1; } +/* + * Helper to copy input string parameter into a query char* parameter + */ +static int set_query_cparam(char** param, str input) +{ + if (*param) { + shm_free(*param); + *param = NULL; + } + + if (input.s && input.len > 0) { + *param = shm_malloc(input.len+1); + + if(*param == NULL) { + LM_ERR("error in shm_malloc\n"); + return -1; + } + + strncpy(*param, input.s, input.len); + (*param)[input.len] = '\0'; + + LM_DBG("param set to '%s'\n", *param); + } + + return 1; +} + /** * */ @@ -678,6 +708,18 @@ static int ah_parse_req_name(pv_spec_p sp, str *in) { sp->pvp.pvn.u.isname.name.n = E_HRN_SUSPEND; else goto error; break; + case 8: + if(strncmp(in->s, "username", 8)==0) + sp->pvp.pvn.u.isname.name.n = E_HRN_USERNAME; + else if(strncmp(in->s, "password", 8)==0) + sp->pvp.pvn.u.isname.name.n = E_HRN_PASSWORD; + else goto error; + break; + case 10: + if(strncmp(in->s, "authmethod", 10)==0) + sp->pvp.pvn.u.isname.name.n = E_HRN_AUTHMETHOD; + else goto error; + break; case 11: if(strncmp(in->s, "tls_ca_path", 11)==0) sp->pvp.pvn.u.isname.name.n = E_HRN_TLS_CA_PATH; @@ -808,6 +850,35 @@ static int ah_set_req(struct sip_msg* msg, pv_param_t *param, set_query_param(&ah_params.body, tval->rs); } break; + case E_HRN_AUTHMETHOD: + if (tval) { + if (!(tval->flags & PV_VAL_INT)) { + LM_ERR("invalid value type for $http_req(authmethod)\n"); + return -1; + } + ah_params.authmethod = tval->ri; + } else { + ah_params.authmethod = default_authmethod; + } + break; + case E_HRN_USERNAME: + if (tval) { + if (!(tval->flags & PV_VAL_STR)) { + LM_ERR("invalid value type for $http_req(username)\n"); + return -1; + } + set_query_cparam(&ah_params.username, tval->rs); + } + break; + case E_HRN_PASSWORD: + if (tval) { + if (!(tval->flags & PV_VAL_STR)) { + LM_ERR("invalid value type for $http_req(password)\n"); + return -1; + } + set_query_cparam(&ah_params.password, tval->rs); + } + break; } return 1; diff --git a/modules/http_async_client/http_multi.c b/modules/http_async_client/http_multi.c index 6704c854fe0..c4781b6bb30 100644 --- a/modules/http_async_client/http_multi.c +++ b/modules/http_async_client/http_multi.c @@ -511,6 +511,18 @@ int new_request(str *query, http_m_params_t *query_params, http_multi_cbe_t cb, default: break; } + + if (cell->params.username) { + curl_easy_setopt(cell->easy, CURLOPT_USERNAME, cell->params.username); + curl_easy_setopt(cell->easy, CURLOPT_HTTPAUTH, cell->params.authmethod); + + LM_DBG("set username to %s [authmethod %u]\n", cell->params.username, cell->params.authmethod); + } + + if (cell->params.password) { + curl_easy_setopt(cell->easy, CURLOPT_PASSWORD, cell->params.password); + } + LM_DBG("Adding easy %p to multi %p (%.*s)\n", cell->easy, g->multi, query->len, query->s); rc = curl_multi_add_handle(g->multi, cell->easy); if (check_mcode(rc, cell->error) < 0) {