1
0
Fork 0

Added Digest HTTP authentication

This commit is contained in:
bagyenda 2007-04-10 17:09:05 +00:00
parent a22898306a
commit 3c990ad7cf
6 changed files with 323 additions and 80 deletions

View File

@ -1,3 +1,5 @@
2007-04-10 P. A. Bagyenda <bagyenda@dsmagic.com>
* Added Digest/MD5 HTTP authentication support (out-going)
2007-04-10 P. A. Bagyenda <bagyenda@dsmagic.com>
* MM7/SOAP XMLNS string now configurable via setting per-MMC (or per-VASP) interface version
2007-04-02 Vincent Chavanis <vincent@telemaque.fr>

View File

@ -1182,3 +1182,313 @@ void strip_boundary_element(List *headers, char *s)
if (value)
octstr_destroy(value);
}
static int fetch_url_with_auth(HTTPCaller *c, int method, Octstr *url, List *request_headers,
Octstr *body, Octstr *auth_hdr, List **reply_headers, Octstr **reply_body);
int mms_url_fetch_content(int method, Octstr *url, List *request_headers,
Octstr *body, List **reply_headers, Octstr **reply_body)
{
int status = 0;
Octstr *furl = NULL;
if (octstr_search(url, octstr_imm("data:"), 0) == 0) {
int i = octstr_search_char(url, ',',0);
Octstr *ctype = (i >= 0) ? octstr_copy(url, 5, i-5) : octstr_create("text/plain; charset=us-ascii");
Octstr *data = (i >= 0) ? octstr_copy(url, i+1, octstr_len(url)) : octstr_duplicate(url);
Octstr *n = NULL, *h = NULL;
if (octstr_len(ctype) == 0)
octstr_append_cstr(ctype, "text/plain; charset=us-ascii");
split_header_value(ctype, &n, &h);
if (h) {
List *ph = get_value_parameters(h);
Octstr *v = NULL;
if (ph && (v = http_header_value(ph, octstr_imm("base64"))) != NULL) { /* has base64 item */
Octstr *p = NULL;
octstr_base64_to_binary(data);
http_header_remove_all(ph, "base64");
octstr_destroy(ctype);
if (gwlist_len(ph) > 0) {
p = make_value_parameters(ph);
ctype = octstr_format("%S; %S",
n,p);
octstr_destroy(p);
} else
ctype = octstr_format("%S", n);
}
if (ph)
http_destroy_headers(ph);
octstr_destroy(v);
octstr_destroy(h);
}
if (n)
octstr_destroy(n);
*reply_body = data;
*reply_headers = http_create_empty_headers();
http_header_add(*reply_headers, "Content-Type", octstr_get_cstr(ctype));
octstr_destroy(ctype);
status = HTTP_OK;
} else {
HTTPCaller *c = http_caller_create();
http_start_request(c, method, url, request_headers, body, 1, NULL, NULL);
if (http_receive_result_real(c, &status, &furl, reply_headers, reply_body,1) == NULL)
status = -1;
if (status == HTTP_UNAUTHORIZED) {
Octstr *v = http_header_value(*reply_headers, octstr_imm("WWW-Authenticate"));
status = fetch_url_with_auth(c, method, url, request_headers, body, v,
reply_headers, reply_body);
octstr_destroy(v);
}
http_caller_destroy(c);
}
if (furl)
octstr_destroy(furl);
return status;
}
Octstr *get_stripped_param_value(Octstr *value, Octstr *param)
{
Octstr *x = http_get_header_parameter(value, param);
if (x != NULL &&
octstr_get_char(x, 0) == '"' &&
octstr_get_char(x, octstr_len(x) - 1) == '"') {
octstr_delete(x, 0, 1);
octstr_delete(x, octstr_len(x) - 1, 1);
}
return x;
}
#define HASHLEN 16
static Octstr *make_url(HTTPURLParse *h);
/* Fetch a url with authentication as necessary. */
static int fetch_url_with_auth(HTTPCaller *c, int method, Octstr *url, List *request_headers,
Octstr *body, Octstr *auth_hdr, List **reply_headers, Octstr **reply_body)
{
Octstr *xauth_value = auth_hdr ? octstr_duplicate(auth_hdr) : octstr_create("");
Octstr *domain = NULL, *nonce = NULL, *opaque = NULL, *algo = NULL, *auth_type = NULL, *x;
Octstr *realm = NULL, *xurl = NULL;
Octstr *cnonce = NULL;
char *nonce_count = "00000001";
Octstr *A1 = NULL, *A2 = NULL, *rd = NULL;
List *qop = NULL, *l = NULL;
int i, status = HTTP_UNAUTHORIZED;
HTTPURLParse *h = parse_url(url);
unsigned char mdbuf[HASHLEN*2], *xs;
char *m_qop = NULL;
time_t t = time(NULL);
/* Check that there is a username and password in the URL! */
if (h == NULL || h->user == NULL || octstr_len(h->user) == 0)
goto done;
/* First we get the auth type: */
if ((i = octstr_search_char(xauth_value, ' ', 0)) < 0) {
warning(0, "Mal-formed WWW-Authenticate header (%s) received while fetching %s!",
octstr_get_cstr(xauth_value), url ? octstr_get_cstr(url) : "");
status = -1;
goto done;
}
auth_type = octstr_copy(xauth_value, 0, i);
octstr_delete(xauth_value, 0, i+1);
if (octstr_str_case_compare(auth_type, "Basic") == 0) {
status = HTTP_UNAUTHORIZED; /* suported by default by GWLIB so if we get here, means bad passwd. */
goto done;
} /* else digest. */
/* Put back some fake data so what we have can be parsed easily. */
if ((l = http_header_split_auth_value(xauth_value)) != NULL) {
Octstr *x = gwlist_get(l, 0);
octstr_insert(x, octstr_imm("_none; "), 0); /* make it easier to parse. */
octstr_destroy(xauth_value);
xauth_value = x;
} else
warning(0, "Mal-formed Digest header (%s) while fetching (%s)!",
octstr_get_cstr(xauth_value), url ? octstr_get_cstr(url) : "");
realm = get_stripped_param_value(xauth_value, octstr_imm("realm"));
domain = get_stripped_param_value(xauth_value, octstr_imm("domain"));
nonce = get_stripped_param_value(xauth_value, octstr_imm("nonce"));
opaque = get_stripped_param_value(xauth_value, octstr_imm("opaque"));
algo = get_stripped_param_value(xauth_value, octstr_imm("algorithm"));
if ((x = get_stripped_param_value(xauth_value, octstr_imm("qop"))) != NULL) {
qop = octstr_split(x, octstr_imm(","));
octstr_destroy(x);
}
mdbuf[0] = 0;
/* from here on, libssl is required. */
#ifdef HAVE_LIBSSL
if (qop ||
(algo != NULL && octstr_str_case_compare(algo, "MD5-sess") == 0)) {
unsigned char *x = MD5((void *)&t, sizeof t, (void *)mdbuf);
cnonce = octstr_create_from_data((void *)x, HASHLEN);
octstr_binary_to_hex(cnonce,0);
}
/* Make A1 */
x = octstr_format("%S:%S:%S",
h->user, realm, h->pass ? h->pass : octstr_imm(""));
// memset(mdbuf, 0, sizeof mdbuf);
xs = MD5((void *)octstr_get_cstr(x), octstr_len(x), (void *)mdbuf);
A1 = octstr_create_from_data((char *)xs, HASHLEN);
octstr_destroy(x);
if (algo != NULL && octstr_str_case_compare(algo, "MD5-sess") == 0) {
x = octstr_format("%S:%S:%S",
A1, nonce, cnonce);
// memset(mdbuf, 0, sizeof mdbuf);
xs = MD5((void *)octstr_get_cstr(x), octstr_len(x), (void *)mdbuf);
octstr_destroy(A1);
A1 = octstr_create_from_data((char *)xs, HASHLEN);
octstr_destroy(x);
}
octstr_binary_to_hex(A1,0);
/* Make A2. */
x = octstr_format("%s:%S",
http_method2name(method),
h->path);
if (qop != NULL && /* if qop, and qop=auth-int */
gwlist_search(qop, "auth-int",
(gwlist_item_matches_t *)octstr_str_case_compare) != NULL &&
gwlist_search(qop, "auth",
(gwlist_item_matches_t *)octstr_str_case_compare) == NULL) {
Octstr *y;
m_qop = "auth-int";
// memset(mdbuf, 0, sizeof mdbuf);
xs = MD5((void *)octstr_get_cstr(body), octstr_len(body), (void *)mdbuf);
y = octstr_create_from_data((char *)xs, HASHLEN);
octstr_binary_to_hex(y,0);
octstr_append_char(x, ':');
octstr_append(x, y);
octstr_destroy(y);
} else if (qop)
m_qop = "auth";
// memset(mdbuf, 0, sizeof mdbuf);
xs = MD5((void *)octstr_get_cstr(x), octstr_len(x), (void *)mdbuf);
A2 = octstr_create_from_data((char *)xs, HASHLEN);
octstr_destroy(x);
octstr_binary_to_hex(A2,0);
/* Finally make the digest response */
if (qop)
x = octstr_format("%S:%S:%s:%S:%s:%S",
A1, nonce, nonce_count, cnonce,
m_qop, A2);
else
x = octstr_format("%S:%S:%S", A1, nonce, A2);
// memset(mdbuf, 0, sizeof mdbuf);
xs = MD5((void *)octstr_get_cstr(x), octstr_len(x), (void *)mdbuf);
octstr_destroy(x);
rd = octstr_create_from_data((char *)xs, HASHLEN);
octstr_binary_to_hex(rd, 0);
/* make the header value */
x = octstr_format("Digest username=\"%S\", realm=\"%S\", response=\"%S\", nonce=\"%S\", uri=\"%S\"",
h->user, realm, rd, nonce, h->path);
if (opaque)
octstr_format_append(x, ", opaque=\"%S\"", opaque);
if (cnonce)
octstr_format_append(x, ", cnonce=\"%S\", nc=%s", cnonce, nonce_count);
if (m_qop)
octstr_format_append(x,", qop=%s", m_qop);
if (algo)
octstr_format_append(x,", algorithm=%S", algo);
http_header_remove_all(request_headers, "Authorization");
http_header_add(request_headers, "Authorization", octstr_get_cstr(x));
octstr_destroy(x);
/* Remove username, password, then remake URL */
if (h->user) octstr_destroy(h->user);
h->user = NULL;
if (h->pass) octstr_destroy(h->pass);
h->pass = NULL;
xurl = make_url(h);
x = NULL;
http_start_request(c, method, xurl, request_headers, body, 1, NULL, NULL);
if (http_receive_result_real(c, &status, &x, reply_headers, reply_body,1) == NULL)
status = -1;
if (x)
octstr_destroy(x);
#else
error(0, "Digest authentication requested on url (%s), but SSL not compiled!",
octstr_get_cstr(url));
#endif
done:
octstr_destroy(xauth_value);
if (realm)
octstr_destroy(realm);
if (domain)
octstr_destroy(domain);
if (nonce)
octstr_destroy(nonce);
if (opaque)
octstr_destroy(opaque);
if (algo)
octstr_destroy(algo);
if (xurl)
octstr_destroy(xurl);
if (qop)
gwlist_destroy(qop, (gwlist_item_destructor_t *)octstr_destroy);
if (h)
http_urlparse_destroy(h);
if (cnonce)
octstr_destroy(cnonce);
return status;
}
static Octstr *make_url(HTTPURLParse *h)
{
Octstr *url = octstr_duplicate(h->scheme);
if (h->user) {
octstr_format_append(url, "%S", h->user);
if (h->pass)
octstr_format_append(url, ":%S", h->pass);
octstr_format_append(url, "@");
}
octstr_format_append(url, "%S:%d%S", h->host, h->port, h->path);
if (h->query)
octstr_format_append(url, "?%S", h->query);
if (h->fragment)
octstr_format_append(url, "#%S", h->fragment);
return url;
}

View File

@ -168,6 +168,10 @@ Octstr *_x_get_content_id(List *headers);
/* Remove the boundary element from a list of headers. */
void strip_boundary_element(List *headers, char *s);
/* Fetch a URL. If necessary, authenticate, etc. also understands data: url scheme. */
int mms_url_fetch_content(int method, Octstr *url, List *request_headers,
Octstr *body, List **reply_headers, Octstr **reply_body);
#define MAXQTRIES 100
#define BACKOFF_FACTOR 5*60 /* In seconds */
#define QUEUERUN_INTERVAL 1*60 /* 1 minutes. */

View File

@ -579,74 +579,6 @@ int main(int argc, char *argv[])
return 0;
}
int mmsbox_url_fetch_content(int method, Octstr *url, List *request_headers,
Octstr *body, List **reply_headers, Octstr **reply_body)
{
int status = 0;
Octstr *furl = NULL;
if (octstr_search(url, octstr_imm("data:"), 0) == 0) {
int i = octstr_search_char(url, ',',0);
Octstr *ctype = (i >= 0) ? octstr_copy(url, 5, i-5) : octstr_create("text/plain; charset=us-ascii");
Octstr *data = (i >= 0) ? octstr_copy(url, i+1, octstr_len(url)) : octstr_duplicate(url);
Octstr *n = NULL, *h = NULL;
if (octstr_len(ctype) == 0)
octstr_append_cstr(ctype, "text/plain; charset=us-ascii");
split_header_value(ctype, &n, &h);
if (h) {
List *ph = get_value_parameters(h);
Octstr *v = NULL;
if (ph && (v = http_header_value(ph, octstr_imm("base64"))) != NULL) { /* has base64 item */
Octstr *p = NULL;
octstr_base64_to_binary(data);
http_header_remove_all(ph, "base64");
octstr_destroy(ctype);
if (gwlist_len(ph) > 0) {
p = make_value_parameters(ph);
ctype = octstr_format("%S; %S",
n,p);
octstr_destroy(p);
} else
ctype = octstr_format("%S", n);
}
if (ph)
http_destroy_headers(ph);
octstr_destroy(v);
octstr_destroy(h);
}
if (n)
octstr_destroy(n);
*reply_body = data;
*reply_headers = http_create_empty_headers();
http_header_add(*reply_headers, "Content-Type", octstr_get_cstr(ctype));
octstr_destroy(ctype);
status = HTTP_OK;
} else {
HTTPCaller *c = http_caller_create();
http_start_request(c, method, url, request_headers, body, 1, NULL, NULL);
if (http_receive_result_real(c, &status, &furl, reply_headers, reply_body,1) == NULL)
status = -1;
http_caller_destroy(c);
}
if (furl)
octstr_destroy(furl);
return status;
}
/* Mapping file extensions to content types. */
static struct {

View File

@ -21,7 +21,6 @@ void mms_dlr_url_remove(Octstr *msgid, char *rtype, Octstr *mmc_gid);
void mmsc_receive_func(MmscGrp *m);
void mmsbox_outgoing_queue_runner(int *rstop);
/* Fetch content given method. also understands data: url scheme. */
int mmsbox_url_fetch_content(int method, Octstr *url, List *request_headers,
Octstr *body, List **reply_headers, Octstr **reply_body);
/* Just a convenience, should go away in future! */
#define mmsbox_url_fetch_content mms_url_fetch_content
#endif

View File

@ -534,8 +534,7 @@ static int mm7soap_send(MmsVasp *vasp, Octstr *from, Octstr *to, Octstr *msgId,
List *xto = gwlist_create();
MSoapMsg_t *mreq = NULL, *mresp = NULL;
List *rh = NULL, *ph = NULL;
Octstr *body = NULL, *rbody = NULL, *url = NULL;
HTTPCaller *caller = http_caller_create();
Octstr *body = NULL, *rbody = NULL;
void *xx;
Octstr *s;
@ -558,11 +557,10 @@ static int mm7soap_send(MmsVasp *vasp, Octstr *from, Octstr *to, Octstr *msgId,
*error = octstr_format("Failed to convert SOAP message 2 HTTP Msg!");
goto done1;
}
http_start_request(caller, HTTP_METHOD_POST, vasp->vasp_url, rh, body, 1, NULL, NULL);
hstatus = mms_url_fetch_content(HTTP_METHOD_POST, vasp->vasp_url, rh, body, &ph, &rbody);
if ((xx = http_receive_result_real(caller, &hstatus, &url, &ph, &rbody,1)) == NULL ||
http_status_class(hstatus) != HTTP_STATUS_SUCCESSFUL) {
if (http_status_class(hstatus) != HTTP_STATUS_SUCCESSFUL) {
*error = octstr_format("Failed to contact VASP[url=%s] => HTTP returned status = %d, id=%s !",
octstr_get_cstr(vasp->vasp_url), hstatus, xx ? "Ok" : "not OK");
goto done1;
@ -616,9 +614,7 @@ static int mm7soap_send(MmsVasp *vasp, Octstr *from, Octstr *to, Octstr *msgId,
http_destroy_headers(ph);
if (rbody)
octstr_destroy(rbody);
if (url)
octstr_destroy(url);
http_caller_destroy(caller);
gwlist_destroy(xto, NULL);
return ret;