#include "config.h" #include <stdio.h> #ifdef HAVE_STDLIB_H #include <stdlib.h> #endif #ifdef HAVE_MEMORY_H #include <memory.h> #endif #include <curl/curl.h> #include "auth_client.h" static const char *kAuthApiPath = "/api/v1/auth"; struct auth_client { CURL *c; const char *service; const char *server; }; static void auth_client_set_proto(auth_client_t ac, const char *proto) { char url[strlen(ac->server) + 32]; sprintf(url, "%s://%s%s", proto, ac->server, kAuthApiPath); curl_easy_setopt(ac->c, CURLOPT_URL, url); } auth_client_t auth_client_new(const char *service, const char *server) { auth_client_t ac = (auth_client_t)malloc(sizeof(struct auth_client)); ac->service = service; ac->server = server; ac->c = curl_easy_init(); curl_easy_setopt(ac->c, CURLOPT_NOSIGNAL, 1); curl_easy_setopt(ac->c, CURLOPT_TIMEOUT, 60); auth_client_set_proto(ac, "http"); return ac; } void auth_client_set_certificate(auth_client_t ac, const char *ca_file, const char *crt_file, const char *key_file) { curl_easy_setopt(ac->c, CURLOPT_CAPATH, ca_file); curl_easy_setopt(ac->c, CURLOPT_SSLCERTTYPE, "PEM"); curl_easy_setopt(ac->c, CURLOPT_SSLCERT, crt_file); curl_easy_setopt(ac->c, CURLOPT_SSLKEYTYPE, "PEM"); curl_easy_setopt(ac->c, CURLOPT_SSLKEY, key_file); curl_easy_setopt(ac->c, CURLOPT_SSL_VERIFYPEER, 1); curl_easy_setopt(ac->c, CURLOPT_SSL_VERIFYHOST, 0); auth_client_set_proto(ac, "https"); } void auth_client_free(auth_client_t ac) { curl_easy_cleanup(ac->c); free(ac); } const char *auth_client_strerror(int err) { if (err < AC_ERR_CURL_BASE) { return curl_easy_strerror(auth_client_err_to_curl(err)); } switch (err) { case AC_ERR_AUTHENTICATION_FAILURE: return "Authentication failure"; case AC_ERR_OTP_REQUIRED: return "OTP required"; default: return "Unknown error"; } } struct responsebuf { char *buf; int size; }; static void responsebuf_init(struct responsebuf *rbuf) { rbuf->buf = (char *)malloc(1); rbuf->size = 0; } static void responsebuf_free(struct responsebuf *rbuf) { free(rbuf->buf); } 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; 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; return realsize; } 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; 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); if (password) { curl_formadd(&formpost, &formlast, CURLFORM_COPYNAME, "password", CURLFORM_COPYCONTENTS, password, CURLFORM_END); } if (otp_token) { curl_formadd(&formpost, &formlast, CURLFORM_COPYNAME, "otp", CURLFORM_COPYCONTENTS, otp_token, CURLFORM_END); } if (source_ip) { curl_formadd(&formpost, &formlast, CURLFORM_COPYNAME, "source_ip", CURLFORM_COPYCONTENTS, source_ip, CURLFORM_END); } curl_easy_setopt(ac->c, CURLOPT_HTTPPOST, formpost); responsebuf_init(&rbuf); curl_easy_setopt(ac->c, CURLOPT_WRITEFUNCTION, responsebuf_callback); curl_easy_setopt(ac->c, CURLOPT_WRITEDATA, (void *)&rbuf); res = curl_easy_perform(ac->c); if (res == CURLE_OK) { // Check the auth server response. if (!strncmp(rbuf.buf, "OK", 2)) { retval = AC_OK; } else if (!strncmp(rbuf.buf, "OTP_REQUIRED", 12)) { retval = AC_ERR_OTP_REQUIRED; } else if (!strncmp(rbuf.buf, "ERROR", 5)) { retval = AC_ERR_AUTHENTICATION_FAILURE; } else { retval = AC_ERR_BAD_RESPONSE; } } else { retval = auth_client_err_from_curl(res); } curl_formfree(formpost); responsebuf_free(&rbuf); return retval; }