X-Git-Url: https://git.distorted.org.uk/~mdw/become/blobdiff_plain/9e5602f0603b771fdda16731ab5bf6e460fe8795..1554751791fdc733a5f106915f684a31e419797b:/src/check.c diff --git a/src/check.c b/src/check.c index 7e61433..f6560d5 100644 --- a/src/check.c +++ b/src/check.c @@ -1,10 +1,10 @@ /* -*-c-*- * - * $Id: check.c,v 1.5 1997/09/26 09:14:58 mdw Exp $ + * $Id: check.c,v 1.14 2004/04/17 10:46:08 mdw Exp $ * * Check validity of requests * - * (c) 1997 EBI + * (c) 1998 EBI */ /*----- Licensing notice --------------------------------------------------* @@ -26,29 +26,6 @@ * Inc., 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA. */ -/*----- Revision history --------------------------------------------------* - * - * $Log: check.c,v $ - * Revision 1.5 1997/09/26 09:14:58 mdw - * Merged blowfish branch into trunk. - * - * Revision 1.4.2.1 1997/09/26 09:08:01 mdw - * Use the Blowfish encryption algorithm instead of IDEA. This is partly - * because I prefer Blowfish (without any particularly strong evidence) but - * mainly because IDEA is patented and Blowfish isn't. - * - * Revision 1.4 1997/08/07 09:52:05 mdw - * (Log entry for previous version is bogus.) Added support for multiple - * servers. - * - * Revision 1.2 1997/08/04 10:24:20 mdw - * Sources placed under CVS control. - * - * Revision 1.1 1997/07/21 13:47:53 mdw - * Initial revision - * - */ - /*----- Header files ------------------------------------------------------*/ /* --- ANSI headers --- */ @@ -70,29 +47,48 @@ #include +#include #include #include +/* --- mLib headers --- */ + +#include +#include +#include +#include +#include + +/* --- Catacomb headers --- */ + +#include +#include +#include +#include +#include +#include +#include +#include + /* --- Local headers --- */ #include "become.h" -#include "blowfish.h" #include "config.h" -#include "crypt.h" #include "lexer.h" #include "name.h" #include "netg.h" #include "rule.h" -#include "parser.h" -#include "tx.h" +#include "parse.h" #include "userdb.h" -#include "utils.h" -/*----- Main code ---------------------------------------------------------*/ +/*----- Client-end network support ----------------------------------------*/ + +#ifndef NONETWORK /* --- @check__send@ --- * * - * Arguments: @unsigned char *crq@ = pointer to encrypted request + * Arguments: @char *buf@ = pointer to encrypted request + * @size_t sz@ = size of request * @int fd@ = socket to send from * @struct sockaddr_in *serv@ = pointer to table of servers * @size_t n_serv@ = number of servers @@ -104,7 +100,7 @@ * reported. */ -static void check__send(unsigned char *crq, int fd, +static void check__send(char *buf, size_t sz, int fd, struct sockaddr_in *serv, size_t n_serv) { size_t i; @@ -112,7 +108,7 @@ static void check__send(unsigned char *crq, int fd, int err = 0; for (i = 0; i < n_serv; i++) { - if (sendto(fd, (char *)crq, crq_size, 0, + if (sendto(fd, buf, sz, 0, (struct sockaddr *)(serv + i), sizeof(serv[i])) < 0) { T( trace(TRACE_CLIENT, "client: send to %s failed: %s", inet_ntoa(serv[i].sin_addr), strerror(errno)); ) @@ -122,7 +118,7 @@ static void check__send(unsigned char *crq, int fd, } if (!ok) - die("couldn't send request to server: %s", strerror(err)); + die(1, "couldn't send request to server: %s", strerror(err)); } /* --- @check__ask@ --- * @@ -139,173 +135,236 @@ static void check__send(unsigned char *crq, int fd, static int check__ask(request *rq, struct sockaddr_in *serv, size_t n_serv) { + static int tbl[] = { 0, 5, 10, 20, -1 }; + + char buff[2048], rbuff[2048]; + size_t rqlen; + gdsa g; + const char *p; + ghash *h; + key_packdef *kp; + buf b; int fd; - unsigned char crq[crq_size]; - unsigned char sk[BLOWFISH_KEYSIZE]; - time_t t; - pid_t pid; - - /* --- First, build the encrypted request packet --- */ + struct sockaddr_in sin; + socklen_t slen; + ssize_t sz; + int ans; + fd_set fds; + struct timeval start, now, tv; + gdsa_sig s; + key_file f; + key *k; + key_iter ki; + int ind; + size_t i; - { - unsigned char k[BLOWFISH_KEYSIZE]; - FILE *fp; + /* --- Open the public keyring --- */ - /* --- Read in the encryption key --- */ + if ((key_open(&f, file_PUBKEY, KOPEN_READ, key_moan, 0)) != 0) + die(1, "couldn't open public keyring"); - if ((fp = fopen(file_KEY, "r")) == 0) { - die("couldn't open key file `%s': %s", file_KEY, - strerror(errno)); - } - tx_getBits(k, 128, fp); + /* --- Build the request packet --- */ - /* --- Now build a request packet --- */ - - t = time(0); - pid = getpid(); - crypt_packRequest(rq, crq, t, pid, k, sk); - burn(k); - T( trace(TRACE_CLIENT, "client: encrypted request packet"); ) - } + rand_noisesrc(RAND_GLOBAL, &noise_source); + rand_seed(RAND_GLOBAL, 160); + buf_init(&b, buff, sizeof(buff)); + rand_get(RAND_GLOBAL, buf_get(&b, 64), 64); + buf_putu32(&b, rq->from); + buf_putu32(&b, rq->to); + buf_putu16(&b, strlen(rq->cmd)); + buf_put(&b, rq->cmd, strlen(rq->cmd)); + rqlen = BLEN(&b); /* --- Create my socket --- */ - { - struct sockaddr_in sin; + if ((fd = socket(PF_INET, SOCK_DGRAM, 0)) < 0) + die(1, "couldn't create socket: %s", strerror(errno)); + if (fcntl(fd, F_SETFD, 1) < 0) + die(1, "couldn't set close-on-exec flag for socket: %s", strerror(errno)); - if ((fd = socket(PF_INET, SOCK_DGRAM, 0)) < 0) - die("couldn't create socket: %s", strerror(errno)); + /* --- Bind myself to some address --- */ - /* --- Bind myself to some address --- */ + sin.sin_family = AF_INET; + sin.sin_port = 0; + sin.sin_addr.s_addr = htonl(INADDR_ANY); - sin.sin_family = AF_INET; - sin.sin_port = 0; - sin.sin_addr.s_addr = htonl(INADDR_ANY); + if (bind(fd, (struct sockaddr *)&sin, sizeof(sin)) < 0) + die(1, "couldn't bind socket to address: %s", strerror(errno)); - if (bind(fd, (struct sockaddr *)&sin, sizeof(sin)) < 0) - die("couldn't bind socket to address: %s", strerror(errno)); - } + /* --- Find out when we are --- */ - /* --- Now wait for a reply --- */ + gettimeofday(&start, 0); + ind = 0; - { - fd_set fds; - struct timeval start, now, tv; - int ind; - size_t i; + /* --- Now loop until everything's done --- */ - /* --- State table for waiting for replies --- * + for (;;) { + gettimeofday(&now, 0); + + /* --- If the current timer has expired, find one that hasn't --- * * - * For each number, send off the request to our servers, and wait for - * that many seconds to have elapsed since we started. If the number is - * %$-1$% then it's time to give up. + * Also resend the request after I've found a timer which is still + * extant. If there aren't any, report an error. */ - static int tbl[] = { 0, 5, 10, 20, -1 }; + if (now.tv_sec >= start.tv_sec + tbl[ind] && + now.tv_usec >= start.tv_usec) { + do { + ind++; + if (tbl[ind] < 0) + die(1, "no reply from servers"); + } while (now.tv_sec >= start.tv_sec + tbl[ind] && + now.tv_usec >= start.tv_usec); + check__send(buff, rqlen, fd, serv, n_serv); + T( trace(TRACE_CLIENT, "client: send request to servers"); ) + } + + /* --- Now wait for a packet to arrive --- */ - /* --- Find out when we are --- */ + if (now.tv_usec > start.tv_usec) { + now.tv_usec -= 1000000; + now.tv_sec += 1; + } + tv.tv_sec = start.tv_sec + tbl[ind] - now.tv_sec; + tv.tv_usec = start.tv_usec - now.tv_usec; - gettimeofday(&start, 0); - ind = 0; + /* --- Sort out file descriptors to watch --- */ - /* --- Now loop until everything's done --- */ + FD_ZERO(&fds); + FD_SET(fd, &fds); - for (;;) { - gettimeofday(&now, 0); + /* --- Wait for them --- */ - /* --- If the current timer has expired, find one that hasn't --- * - * - * Also resend the request after I've found a timer which is still - * extant. If there aren't any, report an error. - */ + i = select(FD_SETSIZE, &fds, 0, 0, &tv); + if (i == 0 || (i < 0 && errno == EINTR)) + continue; + if (i < 0) + die(1, "error waiting for reply: %s", strerror(errno)); - if (now.tv_sec >= start.tv_sec + tbl[ind] && - now.tv_usec >= start.tv_usec) { - do { - ind++; - if (tbl[ind] < 0) - die("no reply from servers"); - } while (now.tv_sec >= start.tv_sec + tbl[ind] && - now.tv_usec >= start.tv_usec); - check__send(crq, fd, serv, n_serv); - T( trace(TRACE_CLIENT, "client: send request to servers"); ) - } + /* --- Read the reply data --- */ - /* --- Now wait for a packet to arrive --- */ + slen = sizeof(sin); + if ((sz = recvfrom(fd, (char *)rbuff, sizeof(rbuff), 0, + (struct sockaddr *)&sin, &slen)) < 0) + die(1, "error reading server's reply: %s", strerror(errno)); - if (now.tv_usec > start.tv_usec) { - now.tv_usec -= 1000000; - now.tv_sec += 1; - } - tv.tv_sec = start.tv_sec + tbl[ind] - now.tv_sec; - tv.tv_usec = start.tv_usec - now.tv_usec; + IF_TRACING(TRACE_CLIENT, { + struct hostent *h = gethostbyaddr((char *)&sin.sin_addr, + sizeof(sin.sin_addr), AF_INET); + trace(TRACE_CLIENT, "client: reply received from %s port %i", + h ? h->h_name : inet_ntoa(sin.sin_addr), + ntohs(sin.sin_port)); + }) + + /* --- Verify the sender --- * + * + * This is more to avoid confusion than for security: an active + * attacker is quite capable of forging the source address. We rely + * on the signature in the reply packet for authentication. + */ + + for (i = 0; i < n_serv; i++) { + if (sin.sin_addr.s_addr == serv[i].sin_addr.s_addr && + sin.sin_port == serv[i].sin_port) + break; + } + if (i >= n_serv) { + T( trace(TRACE_CLIENT, "client: reply from unknown host"); ) + continue; + } - /* --- Sort out file descriptors to watch --- */ + /* --- The hash length varies with the key --- * + * + * So we have to unpack once for each key. This isn't too bad. + */ - FD_ZERO(&fds); - FD_SET(fd, &fds); + g.r = &rand_global; + g.u = 0; + key_mkiter(&ki, &f); + while ((k = key_next(&ki)) != 0) { + if (key_expired(k)) continue; + if (strcmp(k->type, "become") != 0) continue; - /* --- Wait for them --- */ + /* --- Get a hash function --- */ - i = select(FD_SETSIZE, &fds, 0, 0, &tv); - if (i == 0 || (i < 0 && errno == EINTR)) + if ((p = key_getattr(&f, k, "hash")) == 0) + p = "sha"; + if ((g.h = ghash_byname(p)) == 0) continue; - if (i < 0) - die("error waiting for reply: %s", strerror(errno)); - - /* --- A reply should be waiting now --- */ - - { - struct sockaddr_in sin; - int slen = sizeof(sin); - unsigned char buff[256]; - int answer; - - /* --- Read the reply data --- */ - - if (recvfrom(fd, (char *)buff, sizeof(buff), 0, - (struct sockaddr *)&sin, &slen) < 0) - die("error reading server's reply: %s", strerror(errno)); - - IF_TRACING(TRACE_CLIENT, { - struct hostent *h = gethostbyaddr((char *)&sin.sin_addr, - sizeof(sin.sin_addr), AF_INET); - trace(TRACE_CLIENT, "client: reply received from %s port %i", - h ? h->h_name : inet_ntoa(sin.sin_addr), - ntohs(sin.sin_port)); - }) - - /* --- Verify the sender --- * - * - * This is more to avoid confusion than for security: an active - * attacker is quite capable of forging the source address. We rely - * on the checksum in the reply packet for authentication. - */ - - for (i = 0; i < n_serv; i++) { - if (sin.sin_addr.s_addr == serv[i].sin_addr.s_addr && - sin.sin_port == serv[i].sin_port) - break; - } - if (i >= n_serv) { - T( trace(TRACE_CLIENT, "client: reply from unknown host"); ) - continue; - } - - /* --- Unpack and verify the response --- */ - - answer = crypt_unpackReply(buff, sk, t, pid); - if (answer < 0) { - T( trace(TRACE_CLIENT, - "client: invalid or corrupt reply packet"); ) - continue; - } - return (answer); - } + + /* --- Unpack the key --- */ + + p = key_getattr(&f, k, "sig"); + if (!p || strcmp(p, "dsa") == 0) { + dh_pub dp; + kp = key_fetchinit(dh_pubfetch, 0, &dp); + if (key_fetch(kp, k)) goto fail_1; + if ((g.g = group_prime(&dp.dp)) == 0) goto fail_1; + g.p = G_CREATE(g.g); + if (G_FROMINT(g.g, g.p, dp.y)) goto fail_2; + } else if (strcmp(p, "ecdsa") == 0) { + ec_pub ep; + ec_info ei; + kp = key_fetchinit(ec_pubfetch, 0, &ep); + if (key_fetch(kp, k)) goto fail_1; + if (ec_getinfo(&ei, ep.cstr)) goto fail_1; + g.g = group_ec(&ei); + g.p = G_CREATE(g.g); + if (G_FROMEC(g.g, g.p, &ep.p)) goto fail_2; + } else + goto fail_0; + + /* --- Unpack the response --- */ + + h = GH_INIT(g.h); + GH_HASH(h, buff, rqlen); + buf_init(&b, rbuff, sz); + if (buf_ensure(&b, g.h->hashsz)) goto fail_3; + if (memcmp(BCUR(&b), GH_DONE(h, 0), g.h->hashsz) != 0) goto fail_3; + BSTEP(&b, g.h->hashsz); + if ((ans = buf_getbyte(&b)) < 0) goto fail_3; + GH_DESTROY(h); + + /* --- Verify the signature --- */ + + h = gdsa_beginhash(&g); + GH_HASH(h, BBASE(&b), BLEN(&b)); + gdsa_endhash(&g, h); + s.r = s.s = 0; + if ((s.r = buf_getmp(&b)) == 0 || + (s.s = buf_getmp(&b)) == 0) + goto fail_4; + if (gdsa_verify(&g, &s, GH_DONE(h, 0))) + goto fail_4; + + mp_drop(s.r); mp_drop(s.s); + GH_DESTROY(h); + G_DESTROY(g.g, g.p); + G_DESTROYGROUP(g.g); + key_fetchdone(kp); + key_close(&f); + return (ans); + + /* --- Tidy up and try again --- */ + + fail_4: + mp_drop(s.r); mp_drop(s.s); + fail_3: + GH_DESTROY(h); + G_DESTROY(g.g, g.p); + fail_2: + G_DESTROYGROUP(g.g); + fail_1: + key_fetchdone(kp); + fail_0: + continue; } + + T( trace(TRACE_CLIENT, + "client: invalid or corrupt reply packet"); ) } - die("internal error: can't get here in check__ask"); + die(1, "internal error: can't get here in check__ask"); return (0); } @@ -366,7 +425,7 @@ int check__client(request *rq, FILE *fp) { struct servent *s = getservbyname(quis(), "udp"); - port = (s ? s->s_port : -1); + port = (s ? s->s_port : htons(SERVER_PORT)); } /* --- Initialise for scanning the file --- */ @@ -397,7 +456,7 @@ int check__client(request *rq, FILE *fp) case st_host: if (p == l) - die("string too long in `" file_SERVER "'"); + die(1, "string too long in `" file_SERVER "'"); if (ch != EOF && !isspace((unsigned char)ch) && ch != ':') { *p++ = ch; ch = getc(fp); @@ -406,7 +465,7 @@ int check__client(request *rq, FILE *fp) *p++ = 0; if ((h = gethostbyname(buff)) == 0) - die("unknown host `%s' in `" file_SERVER "'", buff); + die(1, "unknown host `%s' in `" file_SERVER "'", buff); memcpy(&t_host, h->h_addr, sizeof(t_host)); state = st_colon; } @@ -444,7 +503,7 @@ int check__client(request *rq, FILE *fp) case st_port: if (p == l) - die("string too long in `" file_SERVER "'"); + die(1, "string too long in `" file_SERVER "'"); if (ch != EOF && !isspace((unsigned char)ch) && ch != ':') { *p++ = ch; ch = getc(fp); @@ -456,7 +515,7 @@ int check__client(request *rq, FILE *fp) if (!s && isdigit((unsigned char)buff[0])) t_port = htons(atoi(buff)); else if (!s) - die("unknown service `%s' in `" file_SERVER "'", buff); + die(1, "unknown service `%s' in `" file_SERVER "'", buff); else t_port = s->s_port; state = st_commit; @@ -468,7 +527,8 @@ int check__client(request *rq, FILE *fp) case st_commit: if (n_serv == max_serv) { max_serv *= 2; - serv = xrealloc(serv, max_serv * sizeof(*serv)); + serv = xrealloc(serv, n_serv * sizeof(*serv), + max_serv * sizeof(*serv)); } serv[n_serv].sin_family = AF_INET; serv[n_serv].sin_addr = t_host; @@ -482,7 +542,7 @@ int check__client(request *rq, FILE *fp) /* --- A safety net for a broken parser --- */ default: - die("internal error: can't get here in check__client"); + die(1, "internal error: can't get here in check__client"); break; } } @@ -493,7 +553,7 @@ int check__client(request *rq, FILE *fp) /* --- Now start sending requests --- */ if (!n_serv) - die("no servers specified in `" file_SERVER "'"); + die(1, "no servers specified in `" file_SERVER "'"); IF_TRACING(TRACE_CLIENT, { size_t i; @@ -506,6 +566,10 @@ int check__client(request *rq, FILE *fp) return (check__ask(rq, serv, n_serv)); } +#endif + +/*----- Main checking function --------------------------------------------*/ + /* --- @check@ --- * * * Arguments: @request *rq@ = pointer to request buffer @@ -521,13 +585,15 @@ int check(request *rq) /* --- Check if we need to talk to a server --- */ +#ifndef NONETWORK if ((fp = fopen(file_SERVER, "r")) != 0) return (check__client(rq, fp)); +#endif /* --- Otherwise do this all the old-fashioned way --- */ if ((fp = fopen(file_RULES, "r")) == 0) { - die("couldn't read configuration file `%s': %s", + die(1, "couldn't read configuration file `%s': %s", file_RULES, strerror(errno)); } @@ -538,7 +604,8 @@ int check(request *rq) name_init(); rule_init(); lexer_scan(fp); - yyparse(); + parse(); + fclose(fp); return (rule_check(rq)); }