diff options
author | Sergey Poznyakoff <gray@gnu.org.ua> | 2020-02-26 15:48:20 +0200 |
---|---|---|
committer | Sergey Poznyakoff <gray@gnu.org.ua> | 2020-02-27 17:40:57 +0200 |
commit | 3b2d57c14f1ed207c79362b7136e3da4054ad817 (patch) | |
tree | 8f97331e65e3d6cc0d483fc0c3d11a2e668e33be /src/ping903q.c | |
parent | a9a6567bac8ab7729c243b555e7fb3c4bb2c6e51 (diff) | |
download | ping903-3b2d57c14f1ed207c79362b7136e3da4054ad817.tar.gz ping903-3b2d57c14f1ed207c79362b7136e3da4054ad817.tar.bz2 |
Implememt client-side basic auth
* lib/Makefile.am: Add base64.c
* lib/base64.c: New file.
* lib/basicauth.c: Move base64 support to a separate source.
* lib/basicauth.h (base64_encode,base64_decode): New protos.
* src/strsplit.c: New file.
* src/Makefile.am (libping903_a_SOURCES): Add strsplit.c
* src/defs.h (CRED_FILE_NAME): New macro.
(ecalloc,strsplit,argcv_free): New proto.
* src/mem.c (ecalloc): New function.
* src/ping903.c (strsplit): Remove.
(cf_auth): Use modified strsplit.
* src/ping903q.c (http_query): Attempt to authenticate
if basic auth is required.
* examples/lib/LWP/Ping903.pm: New file.
* examples/dbload: Use LWP::Ping903
* examples/inspect: Likewise.
* examples/ipadd: Likewise.
* examples/ipdel: Likewise.
Diffstat (limited to 'src/ping903q.c')
-rw-r--r-- | src/ping903q.c | 302 |
1 files changed, 280 insertions, 22 deletions
diff --git a/src/ping903q.c b/src/ping903q.c index e1faacd..85388f8 100644 --- a/src/ping903q.c +++ b/src/ping903q.c @@ -20,6 +20,8 @@ int verbose; int resolve_ip; char *nagios_prefix_format = "PING"; char const http_version[] = "HTTP/1.1"; +char *cred_file_name; +char *nodename, *service; enum { EX_NAGIOS_OK = 0, @@ -362,7 +364,7 @@ http_recv(struct http_resp *resp) } static void -http_query(char const *meth, char const *url, char **hdr) +http_query_primitive(char const *meth, char const *url, char **hdr) { size_t i; int host = 0; @@ -380,6 +382,272 @@ http_query(char const *meth, char const *url, char **hdr) fprintf(http, "\r\n"); } +static char *std_headers[] = { + "Accept: application/json", + "User-Agent: ping903q (" PACKAGE_STRING ")", + NULL +}; + +#define BASICPREF "Basic realm=\"" +#define BASICLEN (sizeof(BASICPREF)-1) + +static char const * +get_cred_file_name(void) +{ + if (!cred_file_name) { + char const *home = getenv("HOME"); + size_t len = strlen(home) + sizeof(CRED_FILE_NAME); + cred_file_name = emalloc(len + 1); + strcpy(cred_file_name, home); + strcat(cred_file_name, "/"); + strcat(cred_file_name, CRED_FILE_NAME); + } + return cred_file_name; +} + +static int +addrinfocmp(struct addrinfo *aptr, struct addrinfo *bptr) +{ + for (; aptr; aptr = aptr->ai_next) { + for (; bptr; bptr = bptr->ai_next) { + if (aptr->ai_addrlen == bptr->ai_addrlen + && memcmp(aptr->ai_addr, bptr->ai_addr, + bptr->ai_addrlen) == 0) + return 0; + } + } + return 1; +} + +static int +nodecmp(char const *a, char const *b) +{ + struct addrinfo hints, *ares, *bres; + int rc; + + memset(&hints, 0, sizeof(hints)); + hints.ai_family = AF_INET; + hints.ai_protocol = IPPROTO_TCP; + + rc = getaddrinfo(a, NULL, &hints, &ares); + if (rc) + return -1; + + rc = getaddrinfo(b, NULL, &hints, &bres); + if (rc) { + freeaddrinfo(ares); + return -1; + } + + rc = addrinfocmp(ares, bres); + freeaddrinfo(ares); + freeaddrinfo(bres); + return rc; +} + +static int +machinecmp(char const *pat) +{ + size_t slen; + + if (strcmp(pat, "*") == 0) + return 0; + if (pat[0] == '*' && pat[1] == ':') { + pat += 2; + } else { + size_t len = strcspn(pat, ":"); + if (!(len == strlen(nodename) + && memcmp(pat, nodename, len) == 0)) { + int rc; + char *pcopy = emalloc(len + 1); + memcpy(pcopy, pat, len); + pcopy[len] = 0; + rc = nodecmp(pcopy, nodename); + free(pcopy); + if (rc) + return -1; + } + pat += len; + if (*pat == 0) + return 0; + pat++; + } + + if (strcmp(pat, "*") == 0) + return 0; + return strcmp(pat, service); +} + +static int +get_auth_creds(char const *auth, char **retval) +{ + char *realm, *p; + size_t len; + char const *cfname; + FILE *fp; + char buf[1024]; + int ln = 0; + int skip_to_eol = 0; + int ret; + int i; + + if (!auth || strncmp(auth, BASICPREF, BASICLEN)) + return -1; + auth += BASICLEN; + len = strlen(auth); + if (len == 0 || auth[len-1] != '"') + return -1; + len--; + if (auth[len-1] == '\\') + return -1; + realm = emalloc(len + 1); + memcpy(realm, auth, len); + for (i = 0; i < len; ) { + if (*auth == '\\') + auth++; + realm[i++] = *auth++; + } + realm[i] = 0; + + cfname = get_cred_file_name(); + fp = fopen(cfname, "r"); + if (!fp) { + free(realm); + return -1; + } + + ret = -1; + while (fgets(buf, sizeof(buf), fp)) { + char *p; + int ac; + char **av; + char *endp; + + ln++; + len = strlen(buf); + if (len == 0) + continue; + + if (skip_to_eol) { + skip_to_eol = buf[len-1] != '\n'; + continue; + } + + if (buf[len-1] == '\n') + buf[--len] = 0; + else if (!feof(fp)) { + error("%s:%d: line too long", cfname, ln); + skip_to_eol = 1; + continue; + } + + while (len > 0 && isspace(buf[len-1])) + buf[--len] = 0; + + if (len == 0) + continue; + + for (p = buf; (*p == ' ' || *p == '\t'); p++) + ; + if (*p == 0 || *p == '#') + continue; + + switch (strsplit(p, 4, &ac, &av, &endp)) { + case STRSPLIT_OK: + if (!*endp) + break; + /* fall through */ + case STRSPLIT_ERR: + error("%s:%d: syntax error near %s", + cfname, ln, endp); + argcv_free(ac, av); + goto err; + + case STRSPLIT_NOMEM: + emalloc_die(); + } + + if (ac != 4) { + error("%s:%d: not enough fields", cfname, ln); + argcv_free(ac, av); + goto err; + } + + if (machinecmp(av[0]) == 0 + && (strcmp(av[1], "*") == 0 + || strcmp(av[1], realm) == 0)) { + len = strlen(av[2]) + strlen(av[3]) + 2; + char *p = emalloc(len); + strcpy(p, av[2]); + strcat(p, ":"); + strcat(p, av[3]); + *retval = p; + ret = 0; + argcv_free(ac, av); + break; + } else { + argcv_free(ac, av); + } + } +err: + fclose(fp); + free(realm); + return ret; +} + +#define HTTP_AUTHORIZATION "Authorization: Basic " +#define HTTP_AUTHORIZATION_LEN (sizeof(HTTP_AUTHORIZATION)-1) + +static void +http_query(char const *meth, char const *url, char **hdr, + struct http_resp *resp) +{ + http_query_primitive(meth, url, hdr); + http_recv(resp); + if (resp->code != 200) { + if (resp->code == 401) { + char *creds; + char const *auth = + http_resp_get_header(resp, + "WWW-Authenticate"); + if (get_auth_creds(auth, &creds) == 0) { + char *b64creds; + size_t b64creds_len; + char **new_hdr; + size_t i, nhdr; + char *auth_hdr; + + for (nhdr = 0; hdr[nhdr]; nhdr++) + ; + new_hdr = ecalloc(nhdr + 2, + sizeof(new_hdr[0])); + for (i = 0; i < nhdr; i++) + new_hdr[i] = hdr[i]; + + if (base64_encode(creds, strlen(creds), + &b64creds, &b64creds_len)) + emalloc_die(); + free(creds); + + auth_hdr = emalloc(HTTP_AUTHORIZATION_LEN + + b64creds_len + + 1); + strcpy(auth_hdr, HTTP_AUTHORIZATION); + strcat(auth_hdr, b64creds); + free(b64creds); + new_hdr[i] = auth_hdr; + new_hdr[i+1] = NULL; + + http_query_primitive(meth, url, new_hdr); + http_recv(resp); + + free(new_hdr); + free(auth_hdr); + } + } + } +} + static struct json_value * ejson_get(struct json_value *obj, char *name, int type) { @@ -460,12 +728,6 @@ resolve_host(char const *name) return estrdup(hbuf); } -static char *std_headers[] = { - "Accept: application/json", - "User-Agent: ping903q (" PACKAGE_STRING ")", - NULL -}; - static void query_host(char const *name, int (*report)(struct json_value *, void *), void *report_data) @@ -477,8 +739,8 @@ query_host(char const *name, int (*report)(struct json_value *, void *), char const *hval; char *p; char *ipstr = resolve_host(name); - ssize_t n; - + ssize_t n; + if (resolve_ip) { n = snprintf(url, sizeof(url), "/ip/%s", ipstr); } else { @@ -488,12 +750,10 @@ query_host(char const *name, int (*report)(struct json_value *, void *), if (n < 0 || n == sizeof(url)) { abend("bad host name or IP"); } - http_query("GET", url, std_headers); http_resp_init(&resp); - http_recv(&resp); - if (resp.code != 200) { + http_query("GET", url, std_headers, &resp); + if (resp.code != 200) abend("%s", resp.reason); - } hval = http_resp_get_header(&resp, "content-type"); if (!hval || strcmp(hval, "application/json")) { @@ -526,9 +786,8 @@ match_host(char const *name) if (n == -1 || n == sizeof(url)) { abend("url buffer overflow"); } - http_query("GET", url, std_headers); http_resp_init(&resp); - http_recv(&resp); + http_query("GET", url, std_headers, &resp); if (resp.code != 200) { abend("%s", resp.reason); } @@ -575,9 +834,8 @@ query_all(void) size_t len; size_t i; - http_query("GET", "/host", NULL); http_resp_init(&resp); - http_recv(&resp); + http_query("GET", "/host", std_headers, &resp); if (resp.code != 200) { abend("%s", resp.reason); } @@ -783,7 +1041,7 @@ int main(int argc, char **argv) { int c; - char *p, *node, *service; + char *p; char const *host = NULL; char *c_opt = NULL; char *w_opt = NULL; @@ -866,12 +1124,12 @@ main(int argc, char **argv) break; } - p = node = read_listen(&service); - if (!node || !node[0]) - node = DEFAULT_ADDRESS; + p = nodename = read_listen(&service); + if (!nodename || !nodename[0]) + nodename = DEFAULT_ADDRESS; if (!service) service = DEFAULT_SERVICE; - http_connect(node, service); + http_connect(nodename, service); free(p); switch (mode) { |