diff --git a/auth.c b/auth.c index a2130fdd..376b7eb4 100644 --- a/auth.c +++ b/auth.c @@ -41,65 +41,34 @@ #include "openconnect-internal.h" static int xmlpost_append_form_opts(struct openconnect_info *vpninfo, - struct oc_auth_form *form, char *body, int bodylen); + struct oc_auth_form *form, struct oc_text_buf *body); static int can_gen_tokencode(struct openconnect_info *vpninfo, struct oc_auth_form *form, struct oc_form_opt *opt); static int do_gen_tokencode(struct openconnect_info *vpninfo, struct oc_auth_form *form); -static int append_opt(char *body, int bodylen, char *opt, char *name) +static int append_opt(struct oc_text_buf *body, char *opt, char *name) { - int len = strlen(body); + if (buf_error(body)) + return buf_error(body); - if (len) { - if (len >= bodylen - 1) - return -ENOSPC; - body[len++] = '&'; - } - - while (*opt) { - if (isalnum((int)(unsigned char)*opt)) { - if (len >= bodylen - 1) - return -ENOSPC; - body[len++] = *opt; - } else { - if (len >= bodylen - 3) - return -ENOSPC; - sprintf(body+len, "%%%02x", (unsigned char)*opt); - len += 3; - } - opt++; - } + if (body->pos) + buf_append(body, "&"); - if (len >= bodylen - 1) - return -ENOSPC; - body[len++] = '='; - - while (name && *name) { - if (isalnum((int)(unsigned char)*name)) { - if (len >= bodylen - 1) - return -ENOSPC; - body[len++] = *name; - } else { - if (len >= bodylen - 3) - return -ENOSPC; - sprintf(body+len, "%%%02x", (unsigned char)*name); - len += 3; - } - name++; - } - body[len] = 0; + buf_append_urlencoded(body, opt); + buf_append(body, "="); + buf_append_urlencoded(body, name); return 0; } static int append_form_opts(struct openconnect_info *vpninfo, - struct oc_auth_form *form, char *body, int bodylen) + struct oc_auth_form *form, struct oc_text_buf *body) { struct oc_form_opt *opt; int ret; for (opt = form->opts; opt; opt = opt->next) { - ret = append_opt(body, bodylen, opt->name, opt->value); + ret = append_opt(body, opt->name, opt->value); if (ret) return ret; } @@ -790,7 +759,7 @@ int process_auth_form(struct openconnect_info *vpninfo, struct oc_auth_form *for * = OC_FORM_RESULT_LOGGEDIN, when form indicates that login was already successful */ int handle_auth_form(struct openconnect_info *vpninfo, struct oc_auth_form *form, - char *request_body, int req_len, const char **method, + struct oc_text_buf *request_body, const char **method, const char **request_body_type) { int ret; @@ -835,8 +804,8 @@ int handle_auth_form(struct openconnect_info *vpninfo, struct oc_auth_form *form return ret; ret = vpninfo->xmlpost ? - xmlpost_append_form_opts(vpninfo, form, request_body, req_len) : - append_form_opts(vpninfo, form, request_body, req_len); + xmlpost_append_form_opts(vpninfo, form, request_body) : + append_form_opts(vpninfo, form, request_body); if (!ret) { *method = "POST"; *request_body_type = "application/x-www-form-urlencoded"; @@ -932,7 +901,7 @@ static xmlDocPtr xmlpost_new_query(struct openconnect_info *vpninfo, const char return NULL; } -static int xmlpost_complete(xmlDocPtr doc, char *body, int bodylen) +static int xmlpost_complete(xmlDocPtr doc, struct oc_text_buf *body) { xmlChar *mem = NULL; int len, ret = 0; @@ -948,12 +917,7 @@ static int xmlpost_complete(xmlDocPtr doc, char *body, int bodylen) return -ENOMEM; } - if (len > bodylen) - ret = -E2BIG; - else { - memcpy(body, mem, len); - body[len] = 0; - } + buf_append_bytes(body, mem, len); xmlFreeDoc(doc); xmlFree(mem); @@ -961,7 +925,7 @@ static int xmlpost_complete(xmlDocPtr doc, char *body, int bodylen) return ret; } -int xmlpost_initial_req(struct openconnect_info *vpninfo, char *request_body, int req_len, int cert_fail) +int xmlpost_initial_req(struct openconnect_info *vpninfo, struct oc_text_buf *request_body, int cert_fail) { xmlNodePtr root, node; xmlDocPtr doc = xmlpost_new_query(vpninfo, "init", &root); @@ -992,15 +956,15 @@ int xmlpost_initial_req(struct openconnect_info *vpninfo, char *request_body, in if (!node) goto bad; } - return xmlpost_complete(doc, request_body, req_len); + return xmlpost_complete(doc, request_body); bad: - xmlpost_complete(doc, NULL, 0); + xmlpost_complete(doc, NULL); return -ENOMEM; } static int xmlpost_append_form_opts(struct openconnect_info *vpninfo, - struct oc_auth_form *form, char *body, int bodylen) + struct oc_auth_form *form, struct oc_text_buf *body) { xmlNodePtr root, node; xmlDocPtr doc = xmlpost_new_query(vpninfo, "auth-reply", &root); @@ -1053,10 +1017,10 @@ static int xmlpost_append_form_opts(struct openconnect_info *vpninfo, !xmlNewTextChild(root, NULL, XCAST("host-scan-token"), XCAST(vpninfo->csd_token))) goto bad; - return xmlpost_complete(doc, body, bodylen); + return xmlpost_complete(doc, body); bad: - xmlpost_complete(doc, NULL, 0); + xmlpost_complete(doc, NULL); return -ENOMEM; } diff --git a/http.c b/http.c index 0f1ffa54..e4eb621f 100644 --- a/http.c +++ b/http.c @@ -46,6 +46,27 @@ struct oc_text_buf *buf_alloc(void) return calloc(1, sizeof(struct oc_text_buf)); } +void buf_append_urlencoded(struct oc_text_buf *buf, char *str) +{ + while (str && *str) { + if (isalnum((int)(unsigned char)*str)) + buf_append_bytes(buf, str, 1); + else + buf_append(buf, "%%%02x", (unsigned char)*str); + str++; + } +} + +void buf_truncate(struct oc_text_buf *buf) +{ + if (!buf) + return; + + buf->pos = 0; + if (buf->data) + buf->data[0] = 0; +} + int buf_ensure_space(struct oc_text_buf *buf, int len) { int new_buf_len; @@ -890,7 +911,7 @@ static void dump_buf(struct openconnect_info *vpninfo, char prefix, char *buf) * >=0, on success, indicating the length of the data in *form_buf */ static int do_https_request(struct openconnect_info *vpninfo, const char *method, - const char *request_body_type, const char *request_body, + const char *request_body_type, struct oc_text_buf *request_body, char **form_buf, int fetch_redirect) { struct oc_text_buf *buf; @@ -898,6 +919,9 @@ static int do_https_request(struct openconnect_info *vpninfo, const char *method int rq_retry; int rlen, pad; + if (buf_error(request_body)) + return buf_error(request_body); + redirected: vpninfo->redirect_type = REDIR_TYPE_NONE; @@ -920,7 +944,7 @@ static int do_https_request(struct openconnect_info *vpninfo, const char *method add_common_headers(vpninfo, buf); if (request_body_type) { - rlen = strlen(request_body); + rlen = request_body->pos; /* force body length to be a multiple of 64, to avoid leaking * password length. */ @@ -933,7 +957,7 @@ static int do_https_request(struct openconnect_info *vpninfo, const char *method buf_append(buf, "\r\n"); if (request_body_type) - buf_append(buf, "%s", request_body); + buf_append_bytes(buf, request_body->data, request_body->pos); if (vpninfo->port == 443) vpn_progress(vpninfo, PRG_INFO, "%s https://%s/%s\n", @@ -1042,7 +1066,7 @@ int openconnect_obtain_cookie(struct openconnect_info *vpninfo) char *form_buf = NULL; struct oc_auth_form *form = NULL; int result, buflen, tries; - char request_body[2048]; + struct oc_text_buf *request_body = buf_alloc(); const char *request_body_type = "application/x-www-form-urlencoded"; const char *method = "POST"; char *orig_host = NULL, *orig_path = NULL, *form_path = NULL; @@ -1069,7 +1093,7 @@ int openconnect_obtain_cookie(struct openconnect_info *vpninfo) * c) Three redirects without seeing a plausible login form */ newgroup: - result = xmlpost_initial_req(vpninfo, request_body, sizeof(request_body), 0); + result = xmlpost_initial_req(vpninfo, request_body, 0); if (result < 0) goto out; @@ -1086,7 +1110,7 @@ int openconnect_obtain_cookie(struct openconnect_info *vpninfo) tries = 0; vpninfo->xmlpost = 0; request_body_type = NULL; - request_body[0] = 0; + buf_truncate(request_body); method = "GET"; if (orig_host) { openconnect_set_hostname(vpninfo, orig_host); @@ -1150,7 +1174,7 @@ int openconnect_obtain_cookie(struct openconnect_info *vpninfo) _("Server requested SSL client certificate; none was configured\n")); cert_failed = 1; } - result = xmlpost_initial_req(vpninfo, request_body, sizeof(request_body), cert_failed); + result = xmlpost_initial_req(vpninfo, request_body, cert_failed); if (result < 0) goto fail; continue; @@ -1230,8 +1254,8 @@ int openconnect_obtain_cookie(struct openconnect_info *vpninfo) /* Step 5: Ask the user to fill in the auth form; repeat as necessary */ while (1) { - request_body[0] = 0; - result = handle_auth_form(vpninfo, form, request_body, sizeof(request_body), + buf_truncate(request_body); + result = handle_auth_form(vpninfo, form, request_body, &method, &request_body_type); if (result < 0 || result == OC_FORM_RESULT_CANCELLED) goto out; @@ -1297,6 +1321,8 @@ int openconnect_obtain_cookie(struct openconnect_info *vpninfo) fetch_config(vpninfo); out: + buf_free(request_body); + free (orig_host); free (orig_path); diff --git a/openconnect-internal.h b/openconnect-internal.h index 1738da29..7c7d3bcc 100644 --- a/openconnect-internal.h +++ b/openconnect-internal.h @@ -593,11 +593,13 @@ int parse_xml_response(struct openconnect_info *vpninfo, char *response, struct oc_auth_form **form, int *cert_rq); int process_auth_form(struct openconnect_info *vpninfo, struct oc_auth_form *form); -int handle_auth_form(struct openconnect_info *vpninfo, struct oc_auth_form *form, - char *request_body, int req_len, const char **method, +int handle_auth_form(struct openconnect_info *vpninfo, + struct oc_auth_form *form, + struct oc_text_buf *request_body, const char **method, const char **request_body_type); void free_auth_form(struct oc_auth_form *form); -int xmlpost_initial_req(struct openconnect_info *vpninfo, char *request_body, int req_len, int cert_fail); +int xmlpost_initial_req(struct openconnect_info *vpninfo, + struct oc_text_buf *request_body, int cert_fail); int prepare_stoken(struct openconnect_info *vpninfo); /* http.c */ @@ -608,6 +610,8 @@ void __attribute__ ((format (printf, 2, 3))) void buf_append_bytes(struct oc_text_buf *buf, const void *bytes, int len); void buf_append_base64(struct oc_text_buf *buf, const void *bytes, int len); void *openconnect_base64_decode(int *len, const char *in); +void buf_truncate(struct oc_text_buf *buf); +void buf_append_urlencoded(struct oc_text_buf *buf, char *str); int buf_error(struct oc_text_buf *buf); int buf_free(struct oc_text_buf *buf); char *openconnect_create_useragent(const char *base);