diff --git a/pam/auth_client.c b/pam/auth_client.c index 668b25a3e88f9751dcc0cdc4623ffec118758dbb..8c7d559d7a40e431432174221cefe5bff5e65be9 100644 --- a/pam/auth_client.c +++ b/pam/auth_client.c @@ -9,7 +9,7 @@ #include <curl/curl.h> #include "auth_client.h" -static const char *kAuthApiPath = "/api/v1/auth"; +static const char *kAuthApiPath = "/api/1/auth"; struct auth_client { CURL *c; @@ -68,86 +68,130 @@ const char *auth_client_strerror(int err) { } } -struct responsebuf { +/* + * A dynamically sized memory buffer that can be appended to, and will + * grow accordingly. It is optimized to perform well for a specific + * size (the initial allocation). + */ +struct cbuf { char *buf; - int size; + size_t alloc, size; }; -static void responsebuf_init(struct responsebuf *rbuf) { - rbuf->buf = (char *)malloc(1); - rbuf->size = 0; +static void cbuf_init(struct cbuf *cbuf, size_t alloc) { + cbuf->buf = (char *)malloc(alloc); + cbuf->alloc = alloc; + cbuf->size = 0; } -static void responsebuf_free(struct responsebuf *rbuf) { - free(rbuf->buf); +static void cbuf_free(struct cbuf *cbuf) { + free(cbuf->buf); +} + +static void cbuf_append(struct cbuf *cbuf, void *data, size_t size) { + // Resize if necessary. + size_t required_alloc = cbuf->size + size + 1; + if (required_alloc > cbuf->alloc) { + size_t new_alloc = cbuf->alloc; + while (new_alloc < required_alloc) { + new_alloc *= 2; + } + cbuf->buf = (char *)realloc(cbuf->buf, new_alloc); + cbuf->alloc = new_alloc; + } + + // Append data to the buffer. + memcpy(cbuf->buf + cbuf->size, data, size); + cbuf->size += size; + cbuf->buf[cbuf->size] = '\0'; +} + +static char *quote(const char *s) { + char *out = (char *)malloc(strlen(s) * 3 + 1), *optr; + for (optr = out; *s; s++) { + switch (*s) { + case ';': + case '/': + case '?': + case ':': + case '@': + case '&': + case '=': + case '+': + case '$': + case ',': + sprintf(optr, "%%%02X", (int)(*s)); + optr += 3; + break; + default: + *optr++ = *s; + } + } + return out; } static size_t responsebuf_callback(void *contents, size_t size, size_t nmemb, void *userp) { size_t realsize = size * nmemb; - struct responsebuf *rbuf = (struct responsebuf *)userp; + struct cbuf *cbuf = (struct cbuf *)userp; - rbuf->buf = (char *)realloc(rbuf->buf, rbuf->size + realsize + 1); - if (rbuf->buf == NULL) { - return 0; - } - memcpy(rbuf->buf + rbuf->size, contents, realsize); - rbuf->size += realsize; - rbuf->buf[rbuf->size] = 0; + cbuf_append(cbuf, contents, realsize); return realsize; } +static void post_field_add(struct cbuf *form_data, const char *key, const char *value) { + char *quoted_key = quote(key), *quoted_value = quote(value); + if (form_data->size != 0) { + cbuf_append(form_data, "&", 1); + } + cbuf_append(form_data, quoted_key, strlen(quoted_key)); + cbuf_append(form_data, "&", 1); + cbuf_append(form_data, quoted_value, strlen(quoted_value)); + free(quoted_key); + free(quoted_value); +} + int auth_client_authenticate(auth_client_t ac, const char *username, const char *password, const char *otp_token, const char *source_ip) { - struct curl_httppost *formpost = NULL; - struct curl_httppost *formlast = NULL; - struct responsebuf rbuf; + struct curl_slist *headers = NULL; + struct cbuf form; + struct cbuf responsebuf; CURLcode res; int retval; // Build the POST request contents. - curl_formadd(&formpost, &formlast, - CURLFORM_COPYNAME, "service", - CURLFORM_COPYCONTENTS, ac->service, - CURLFORM_END); - curl_formadd(&formpost, &formlast, - CURLFORM_COPYNAME, "username", - CURLFORM_COPYCONTENTS, username, - CURLFORM_END); + cbuf_init(&form, 256); + post_field_add(&form, "service", ac->service); + post_field_add(&form, "username", username); if (password) { - curl_formadd(&formpost, &formlast, - CURLFORM_COPYNAME, "password", - CURLFORM_COPYCONTENTS, password, - CURLFORM_END); + post_field_add(&form, "password", password); } if (otp_token) { - curl_formadd(&formpost, &formlast, - CURLFORM_COPYNAME, "otp", - CURLFORM_COPYCONTENTS, otp_token, - CURLFORM_END); + post_field_add(&form, "otp", otp_token); } if (source_ip) { - curl_formadd(&formpost, &formlast, - CURLFORM_COPYNAME, "source_ip", - CURLFORM_COPYCONTENTS, source_ip, - CURLFORM_END); + post_field_add(&form, "source_ip", source_ip); } - curl_easy_setopt(ac->c, CURLOPT_HTTPPOST, formpost); + curl_easy_setopt(ac->c, CURLOPT_POSTFIELDS, form.buf); + + // Set request headers. + curl_slist_append(headers, "Content-Type: application/x-form-www-urlencoded"); + curl_easy_setopt(ac->c, CURLOPT_HTTPHEADER, headers); - responsebuf_init(&rbuf); + cbuf_init(&responsebuf, 64); curl_easy_setopt(ac->c, CURLOPT_WRITEFUNCTION, responsebuf_callback); - curl_easy_setopt(ac->c, CURLOPT_WRITEDATA, (void *)&rbuf); + curl_easy_setopt(ac->c, CURLOPT_WRITEDATA, (void *)&responsebuf); res = curl_easy_perform(ac->c); if (res == CURLE_OK) { // Check the auth server response. - if (!strncmp(rbuf.buf, "OK", 2)) { + if (!strncmp(responsebuf.buf, "OK", 2)) { retval = AC_OK; - } else if (!strncmp(rbuf.buf, "OTP_REQUIRED", 12)) { + } else if (!strncmp(responsebuf.buf, "OTP_REQUIRED", 12)) { retval = AC_ERR_OTP_REQUIRED; - } else if (!strncmp(rbuf.buf, "ERROR", 5)) { + } else if (!strncmp(responsebuf.buf, "ERROR", 5)) { retval = AC_ERR_AUTHENTICATION_FAILURE; } else { retval = AC_ERR_BAD_RESPONSE; @@ -156,8 +200,9 @@ int auth_client_authenticate(auth_client_t ac, retval = auth_client_err_from_curl(res); } - curl_formfree(formpost); - responsebuf_free(&rbuf); + cbuf_free(&form); + cbuf_free(&responsebuf); + curl_slist_free_all(headers); return retval; }