aboutsummaryrefslogtreecommitdiff
path: root/src/ping903q.c
diff options
context:
space:
mode:
authorSergey Poznyakoff <gray@gnu.org.ua>2020-02-26 15:48:20 +0200
committerSergey Poznyakoff <gray@gnu.org.ua>2020-02-27 17:40:57 +0200
commit3b2d57c14f1ed207c79362b7136e3da4054ad817 (patch)
tree8f97331e65e3d6cc0d483fc0c3d11a2e668e33be /src/ping903q.c
parenta9a6567bac8ab7729c243b555e7fb3c4bb2c6e51 (diff)
downloadping903-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.c302
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) {

Return to:

Send suggestions and report system problems to the System administrator.