/* This file is in the public domain. */ #include #include #include #include #include #include #include #include #include #include #include extern const char *__progname; #include "b64.h" #include "errf.h" #include "panic.h" #include "nested.h" #include "verbose.h" #include "stdio-util.h" #include "keyfiles.h" typedef enum { IPKIND_DEFAULT = 1, IPKIND_v4, IPKIND_v6, } IPKIND; typedef enum { IPACTION_ALLOW = 1, IPACTION_DENY, IPACTION_IGNORE, } IPACTION; typedef enum { CMDACTION_ALLOW = 1, CMDACTION_DENY, } CMDACTION; typedef struct ak_ip AK_IP; typedef struct ak_cmd AK_CMD; typedef struct ak_ikey AK_IKEY; struct ak { unsigned int flags; #define AKF_ERROR 0x00000001 int nkeys; int akeys; AK_IKEY **keys; int nips; int aips; AK_IP **ips; AK_CMD_LIST *cmdlist; } ; struct ak_cmd_list { int refcnt; int ncmds; int acmds; AK_CMD **cmds; STR override; } ; struct ak_ikey { AK_KEY ekey; STR blob; char *comment; } ; struct ak_ip { IPKIND kind; IPACTION action; union { struct in_addr a4; struct in6_addr a6; } ; int width; } ; struct ak_cmd { CMDACTION action; unsigned int flags; #define CMDF_TTY 0x00000001 #define CMDF_NOTTY 0x00000002 #define CMDF_LITERAL 0x00000004 #define CMDF_SHELL 0x00000008 #define CMDF_NEVER 0x00000010 char *str; union { int str_len; /* if CMDF_LITERAL */ regex_t *re_bin; /* if !CMDF_LITERAL */ } ; } ; static int writeit(int fd, const char *data, int len, const char *what, const char *fn) { int n; int first; first = 1; while (len > 0) { n = write(fd,data,len); if (n < 0) { fprintf(errf,"%s: error writing %s %s: %s\n",__progname,what,fn,strerror(errno)); return(0); } if (n == 0) { fprintf(errf,"%s: error writing %s %s: zero-length write\n",__progname,what,fn); return(0); } if (first && (n < len)) { fprintf(errf,"%s: warning: short write writing %s %s (wanted %d, wrote %d) - retrying remainder\n",__progname,what,fn,len,n); first = 0; } data += n; len -= n; } return(1); } static char *read_pubkey_comment(FILE *f, const char *def) { char *cbuf; int l; int n; int c; NESTED void savec(char c) { if (l >= n) cbuf = realloc(cbuf,n=l+16); cbuf[l++] = c; } cbuf = 0; l = 0; n = 0; while (1) { c = getc(f); switch (c) { case '\n': ungetc(c,f); /* fall through */ case EOF: if (l == 0) return(def?strdup(def):0); savec('\0'); return(cbuf); break; case ' ': if (l == 0) break; default: savec(c); if (l >= 80) { savec('.'); savec('.'); savec('.'); savec('\0'); return(cbuf); } break; } } } int load_key_pair( const char *fn_pub, const char *fn_priv, void **pubdp, int *publp, HKALG **algp, void **privdp, int *privlp, char **commentp, char *defcomm, FILE *ef ) { char *pubfn; FILE *pubf; char *privfn; FILE *privf; int rv; if (pubdp) { if (! fn_pub) panic("no fn_pub"); asprintf(&pubfn,"%s.pub",fn_pub); } else { pubfn = 0; } if (privdp) { if (! fn_priv) panic("no fn_priv"); asprintf(&privfn,"%s.priv",fn_priv); } else { privfn = 0; } pubf = 0; privf = 0; rv = 0; do <"ret"> { if (pubdp) { pubf = fopen(pubfn,"r"); if (pubf == 0) { if (ef) fprintf(ef,"can't open public key file %s: %s",pubfn,strerror(errno)); break <"ret">; } } if (privdp) { privf = fopen(privfn,"r"); if (privf == 0) { if (ef) fprintf(ef,"can't open private key file %s: %s",privfn,strerror(errno)); break <"ret">; } } rv = load_key_pair_stdio( pubf, pubfn, privf, privfn, pubdp, publp, algp, privdp, privlp, commentp, defcomm, ef ); } while (0); free(pubfn); free(privfn); if (pubf) fclose(pubf); if (privf) fclose(privf); return(rv); } int load_key_pair_stdio( FILE *pubf, const char *pubfn, FILE *privf, const char *privfn, void **pubdp, int *publp, HKALG **algp, void **privdp, int *privlp, char **commentp, char *defcomm, FILE *ef ) { char kname[64+1]; HKALG *alg; void *pub; int publen; void *priv; int privlen; char *comment; int rv; if (pubdp && !pubf) panic("no pubf"); if (privdp && !privf) panic("no privf"); pub = 0; priv = 0; comment = 0; do <"ret"> { do <"fail"> { if (pubdp) { if (fscanf(pubf,"%64s ",&kname[0]) != 1) { if (ef) fprintf(ef,"can't use public key from %s (missing type)",pubfn); break <"fail">; } alg = (*at_hk.find_c)(&kname[0]); if (alg == 0) { if (ef) fprintf(ef,"can't use public key from %s (unknown algorithm name)",pubfn); break <"fail">; } if (algp) *algp = alg; if (! b64_read_blob(pubf,&pub,&publen)) { if (ef) fprintf(ef,"can't use public key from %s (can't read public-key blob)",pubfn); break <"fail">; } if (! (*alg->checkpub)(pub,publen)) { if (ef) fprintf(ef,"can't use public key from %s (corrupted public-key blob)",pubfn); break <"fail">; } comment = read_pubkey_comment(pubf,0); } if (privdp) { if (! b64_read_blob(privf,&priv,&privlen)) { if (ef) fprintf(ef,"can't use private key from %s (blob unreadable)",privfn); break <"fail">; } } if (pubdp) { *pubdp = pub; *publp = publen; } if (commentp) *commentp = comment ? : defcomm; else free(comment); if (privdp) { *privdp = priv; *privlp = privlen; } rv = 1; break <"ret">; } while (0); free(pub); free(priv); free(comment); rv = 0; break <"ret">; } while (0); return(rv); } int load_key_pub_string( const char *str, const char *name, void **pubdp, int *publp, HKALG **algp, char **commentp, char *defcomm, FILE *ef ) { FILE *pubf; int rv; pubf = fopen_rstr(str,strlen(str)); rv = load_key_pair_stdio(pubf,name,0,0,pubdp,publp,algp,0,0,commentp,defcomm,ef); fclose(pubf); return(rv); } int write_pub_to_fd(const char *what, const char *to, int fd, HKALG *alg, const void *pubdata, int publen, const char *comment) { FILE *f; char *blob; int blen; int rv; f = fopen_alloc(&blob,&blen); fprintf(f,"%s ",alg->name); b64_write_blob(f,pubdata,publen); if (comment) fprintf(f," %s",comment); fprintf(f,"\n"); fclose(f); rv = writeit(fd,blob,blen,what,to); free(blob); return(rv); } int brief_pub_to_fd(const char *what, const char *to, int fd, HKALG *alg, const char *comment) { FILE *f; char *blob; int blen; int rv; f = fopen_alloc(&blob,&blen); fprintf(f,"%s",alg->name); if (comment) fprintf(f," %s",comment); fprintf(f,"\n"); fclose(f); rv = writeit(fd,blob,blen,what,to); free(blob); return(rv); } int write_priv_to_fd(const char *what, const char *to, int fd, const void *privdata, int privlen) { FILE *f; char *blob; int blen; int rv; f = fopen_alloc(&blob,&blen); b64_write_blob(f,privdata,privlen); fprintf(f,"\n"); fclose(f); rv = writeit(fd,blob,blen,what,to); free(blob); return(rv); } void write_key(HKALG *alg, const char *pubfile, void *pubdata, int publen, const char *comment, const char *privfile, void *privdata, int privlen) { char *pubtmp; char *pubfn; char *privtmp; char *privfn; int fd; sigset_t mask; sigset_t omask; NESTED void dofrees(void) { free(pubfn); free(pubtmp); free(privfn); free(privtmp); } pubfn = 0; pubtmp = 0; privfn = 0; privtmp = 0; do <"errexit"> { if (pubfile) { asprintf(&pubfn,"%s.pub",pubfile); asprintf(&pubtmp,"%s.PUB",pubfile); fd = open(pubtmp,O_WRONLY|O_CREAT|O_EXCL,0644); if (fd < 0) { fprintf(errf,"%s: can't create %s: %s\n",__progname,pubtmp,strerror(errno)); break <"errexit">; } if (! write_pub_to_fd("public key temporary file",pubtmp,fd,alg,pubdata,publen,comment)) break <"errexit">; if (fsync(fd) < 0) { fprintf(errf,"%s: error flushing public key temporary file %s: %s\n",__progname,pubtmp,strerror(errno)); break <"errexit">; } if (close(fd) < 0) { fprintf(errf,"%s: error closing public key temporary file %s: %s\n",__progname,pubtmp,strerror(errno)); break <"errexit">; } } if (privfile) { asprintf(&privfn,"%s.priv",privfile); asprintf(&privtmp,"%s.PRIV",privfile); fd = open(privtmp,O_WRONLY|O_CREAT|O_EXCL,0600); if (fd < 0) { fprintf(errf,"%s: can't create %s: %s\n",__progname,privtmp,strerror(errno)); break <"errexit">; } if (! write_priv_to_fd("private key temporary file",privtmp,fd,privdata,privlen)) break <"errexit">; if (fsync(fd) < 0) { fprintf(errf,"%s: error flushing private key temporary file %s: %s\n",__progname,privtmp,strerror(errno)); break <"errexit">; } if (close(fd) < 0) { fprintf(errf,"%s: error closing private key temporary file %s: %s\n",__progname,privtmp,strerror(errno)); break <"errexit">; } } sigemptyset(&mask); sigaddset(&mask,SIGHUP); sigaddset(&mask,SIGINT); sigaddset(&mask,SIGQUIT); sigaddset(&mask,SIGTERM); sigaddset(&mask,SIGTSTP); sigaddset(&mask,SIGTTIN); sigaddset(&mask,SIGTTOU); sigprocmask(SIG_BLOCK,&mask,&omask); if (pubfile) { if (rename(pubtmp,pubfn) < 0) { fprintf(errf,"%s: can't rename %s -> %s: %s\n",__progname,pubtmp,pubfn,strerror(errno)); pubtmp = 0; break <"errexit">; } } if (privfile) { if (rename(privtmp,privfn) < 0) { fprintf(errf,"%s: can't rename %s -> %s: %s\n",__progname,privtmp,privfn,strerror(errno)); privtmp = 0; break <"errexit">; } } sigprocmask(SIG_SETMASK,&omask,0); dofrees(); return; } while (0); if (pubtmp) unlink(pubtmp); if (privtmp) unlink(privtmp); exit(1); } int key_files_exist(const char *pubfile, const char *privfile) { char *s; struct stat stb; if (pubfile) { asprintf(&s,"%s.pub",pubfile); if (stat(s,&stb) >= 0) { free(s); return(1); } free(s); } if (privfile) { asprintf(&s,"%s.priv",privfile); if (stat(s,&stb) >= 0) { free(s); return(1); } free(s); } return(0); } static void skip_to_nl(FILE *f) { int c; do c = getc(f); while ((c != '\n') && (c != EOF)); } static void ak_init(AK *ak) { ak->flags = 0; ak->nkeys = 0; ak->akeys = 0; ak->keys = 0; ak->nips = 0; ak->aips = 0; ak->ips = 0; ak->cmdlist = 0; } static void akcl_deref(AK_CMD_LIST *akcl) { int i; AK_CMD *c; if (akcl) { akcl->refcnt --; if (akcl->refcnt < 0) panic("akcl negative refcnt"); if (akcl->refcnt > 0) return; for (i=akcl->ncmds-1;i>=0;i--) { c = akcl->cmds[i]; if (!(c->flags & CMDF_LITERAL) && c->re_bin) { regfree(c->re_bin); free(c->re_bin); } free(c->str); free(c); } free(akcl->cmds); free_str(akcl->override); free(akcl); } } static void ak_clear(AK *ak) { int i; ak->flags = 0; for (i=ak->nkeys-1;i>=0;i--) { free_str(ak->keys[i]->blob); free(ak->keys[i]->comment); free(ak->keys[i]); } ak->nkeys = 0; for (i=ak->nips-1;i>=0;i--) { free(ak->ips[i]); } ak->nips = 0; akcl_deref(ak->cmdlist); ak->cmdlist = 0; } static void ak_done(AK *ak) { ak_clear(ak); free(ak->keys); free(ak->ips); } static void ak_key_line(AK *ak, const char *preread, FILE *f) { HKALG *alg; void *blob; int bloblen; char *comment; AK_IKEY *k; if (VERB(AUTHKEY)) verb(AUTHKEY,"ak_key_line: input offset %ld\n",ftell(f)); do <"done"> { if (preread) { if (VERB(AUTHKEY)) verb(AUTHKEY,"ak_key_line: pre-read algname %s\n",preread); alg = (*at_hk.find_c)(preread); } else { char algname[64+1]; if (fscanf(f,"%*[ \t]%64[^ \t\n]",&algname[0]) != 1) break <"done">; if (VERB(AUTHKEY)) verb(AUTHKEY,"ak_key_line: read algname %s\n",&algname[0]); alg = (*at_hk.find_c)(&algname[0]); } if (alg == 0) { if (VERB(AUTHKEY)) verb(AUTHKEY,"ak_key_line: alg not found\n"); break <"done">; } fscanf(f,"%*[ \t]"); if (! b64_read_blob(f,&blob,&bloblen)) { if (VERB(AUTHKEY)) verb(AUTHKEY,"ak_key_line: base64 blob read failed\n"); ak->flags |= AKF_ERROR; break <"done">; } do <"done"> { if (! (*alg->checkpub)(blob,bloblen)) { if (VERB(AUTHKEY)) verb(AUTHKEY,"ak_key_line: alg checkpub failed\n"); ak->flags |= AKF_ERROR; break <"done">; } comment = read_pubkey_comment(f,0); k = malloc(sizeof(AK_IKEY)); k->blob.data = blob; k->blob.len = bloblen; k->comment = comment; k->ekey.alg = alg; k->ekey.blob = str_to_rostr(k->blob); k->ekey.comment = comment; if (VERB(AUTHKEY)) verb(AUTHKEY,"ak_key_line: saving key %s\n",comment); if (ak->nkeys >= ak->akeys) ak->keys = realloc(ak->keys,(ak->akeys=ak->nkeys+8)*sizeof(*ak->keys)); ak->keys[ak->nkeys++] = k; skip_to_nl(f); if (VERB(AUTHKEY)) verb(AUTHKEY,"ak_key_line: (success) output offset %ld\n",ftell(f)); return; } while (0); free(blob); } while (0); skip_to_nl(f); if (VERB(AUTHKEY)) verb(AUTHKEY,"ak_key_line: (failure) output offset %ld\n",ftell(f)); } static void ak_clientip_line(AK *ak, FILE *f) { char action[64+1]; char arg[64+1]; AK_IP ip; AK_IP *ipp; int maxwidth; if (VERB(AUTHKEY)) verb(AUTHKEY,"ak_clientip_line: input offset %ld\n",ftell(f)); if (fscanf(f,"%*[ \t]%64[^ \t\n]%*[ \t]%64[^ \t\n]",&action[0],&arg[0]) != 2) { if (VERB(AUTHKEY)) verb(AUTHKEY,"ak_clientip_line: can't scan action and IP\n"); ak->flags |= AKF_ERROR; return; } skip_to_nl(f); if (VERB(AUTHKEY)) verb(AUTHKEY,"ak_clientip_line: output offset %ld, action %s, arg %s\n",ftell(f),&action[0],&arg[0]); if (!strcmp(&action[0],"allow")) ip.action = IPACTION_ALLOW; else if (!strcmp(&action[0],"deny")) ip.action = IPACTION_DENY; else if (!strcmp(&action[0],"ignore")) ip.action = IPACTION_IGNORE; else { if (VERB(AUTHKEY)) verb(AUTHKEY,"ak_clientip_line: unrecognized action `%s'\n",&action[0]); ak->flags |= AKF_ERROR; return; } if (!strcmp(&arg[0],"default")) { ip.kind = IPKIND_DEFAULT; } else { char *slash; slash = index(&arg[0],'/'); if (slash) { *slash++ = '\0'; ip.width = atoi(slash); } if (inet_pton(AF_INET,&arg[0],&ip.a4) == 1) { ip.kind = IPKIND_v4; maxwidth = 32; } else if (inet_pton(AF_INET6,&arg[0],&ip.a6) == 1) { ip.kind = IPKIND_v6; maxwidth = 128; } else { if (VERB(AUTHKEY)) verb(AUTHKEY,"ak_clientip_line: skipping unrecognized address format `%s'\n",&arg[0]); ak->flags |= AKF_ERROR; return; } if (slash) { if ((ip.width < 0) || (ip.width > maxwidth)) { if (VERB(AUTHKEY)) verb(AUTHKEY,"ak_clientip_line: bad width %d (of 0..%d)\n",ip.width,maxwidth); ak->flags |= AKF_ERROR; return; } } else { ip.width = maxwidth; } } ipp = malloc(sizeof(AK_IP)); *ipp = ip; if (ak->nips >= ak->aips) ak->ips = realloc(ak->ips,(ak->aips=ak->nips+8)*sizeof(*ak->ips)); ak->ips[ak->nips++] = ipp; } static char *rest_of_line(FILE *f) { char *b; int n; int a; int c; void savec(int ch) { if (n >= a) b = realloc(b,a=n+8); b[n++] = ch; } b = 0; a = 0; n = 0; while (1) { c = getc(f); switch (c) { case '\n': case EOF: savec('\0'); return(b); break; case ' ': case '\t': if (n == 0) continue; /* fall through */ default: savec(c); break; } } } static AK_CMD_LIST *cmdlist_for_ak(AK *ak) { AK_CMD_LIST *cl; cl = ak->cmdlist; if (! cl) { cl = malloc(sizeof(AK_CMD_LIST)); ak->cmdlist = cl; cl->refcnt = 1; cl->ncmds = 0; cl->acmds = 0; cl->cmds = 0; cl->override.len = 0; cl->override.data = 0; } return(cl); } static void ak_command_line(AK *ak, FILE *f) { char action[64+1]; AK_CMD cmd; AK_CMD *cmdp; char *body; int bx; int i; AK_CMD_LIST *cl; void skipws(void) { while (body[bx] && isspace((unsigned char)body[bx])) bx ++; } if (VERB(AUTHKEY)) verb(AUTHKEY,"ak_command_line: input offset %ld\n",ftell(f)); if (fscanf(f,"%*[ \t]%64[^ \t\n]",&action[0]) != 1) return; if (VERB(AUTHKEY)) verb(AUTHKEY,"ak_command_line: action %s\n",&action[0]); if (!strcmp(&action[0],"allow")) cmd.action = CMDACTION_ALLOW; else if (!strcmp(&action[0],"deny")) cmd.action = CMDACTION_DENY; else { skip_to_nl(f); if (VERB(AUTHKEY)) verb(AUTHKEY,"ak_command_line: bad action\n"); ak->flags |= AKF_ERROR; return; } cmd.flags = 0; cmd.str = 0; fscanf(f,"%*[ \t]"); body = rest_of_line(f); if (VERB(AUTHKEY)) verb(AUTHKEY,"ak_command_line: body %s\n",body); bx = 0; switch (cmd.action) { case CMDACTION_ALLOW: case CMDACTION_DENY: while (1) { skipws(); for (i=bx;body[i]&&!isspace((unsigned char)body[i]);i++) ; if ((i-bx == 3) && !bcmp(body+bx,"TTY",3)) { cmd.flags = (cmd.flags & ~CMDF_NOTTY) | CMDF_TTY; if (VERB(AUTHKEY)) verb(AUTHKEY,"ak_command_line: flag TTY\n"); } else if ((i-bx == 5) && !bcmp(body+bx,"NOTTY",5)) { cmd.flags = (cmd.flags & ~CMDF_TTY) | CMDF_NOTTY; if (VERB(AUTHKEY)) verb(AUTHKEY,"ak_command_line: flag NOTTY\n"); } else if ((i-bx == 7) && !bcmp(body+bx,"LITERAL",7)) { cmd.flags |= CMDF_LITERAL; if (VERB(AUTHKEY)) verb(AUTHKEY,"ak_command_line: flag LITERAL\n"); } else { break; } bx = i; } break; default: panic("ak_command_line: impossible action %d",(int)cmd.action); break; } if (! (cmd.flags & CMDF_LITERAL)) cmd.re_bin = 0; skipws(); if (VERB(AUTHKEY)) verb(AUTHKEY,"ak_command_line: command string %s\n",body+bx); if (! strcmp(body+bx,"SHELL")) { cmd.str = 0; cmd.flags |= CMDF_SHELL; } else { cmd.str = strdup(body+bx); if (cmd.flags & CMDF_LITERAL) cmd.str_len = strlen(cmd.str); } free(body); cmdp = malloc(sizeof(AK_CMD)); *cmdp = cmd; cl = cmdlist_for_ak(ak); if (cl->ncmds >= cl->acmds) cl->cmds = realloc(cl->cmds,(cl->acmds=cl->ncmds+8)*sizeof(*cl->cmds)); cl->cmds[cl->ncmds++] = cmdp; } static void ak_override_line(AK *ak, FILE *f) { char *body; int bx; AK_CMD_LIST *cl; if (VERB(AUTHKEY)) verb(AUTHKEY,"ak_override_line: input offset %ld\n",ftell(f)); body = rest_of_line(f); for (bx=0;body[bx]&&isspace((unsigned char)body[bx]);bx++) ; if (! body[bx]) { if (VERB(AUTHKEY)) verb(AUTHKEY,"ak_override_line: no command given\n"); ak->flags |= AKF_ERROR; return; } if (VERB(AUTHKEY)) verb(AUTHKEY,"ak_override_line: command string %s\n",body+bx); cl = cmdlist_for_ak(ak); free_str(cl->override); cl->override = blk_to_str(body+bx,strlen(body+bx)); } int read_authkeys_file(const char *file, int (*callback)(AK *)) { FILE *f; char kname[64+1]; int rv; AK ak; int in_parens; NESTED int do_callback(void) { int v; if (ak.flags & AKF_ERROR) { verb(AUTHKEY,"read_authkeys_file: not calling callback, clause has errors\n"); return(0); } if (VERB(AUTHKEY)) verb(AUTHKEY,"read_authkeys_file: calling callback\n"); v = (*callback)(&ak); if (v < 0) { rv = v; return(1); } rv += v; return(0); } if (VERB(AUTHKEY)) verb(AUTHKEY,"read_authkeys_file: file is %s\n",file); f = fopen(file,"r"); if (f == 0) { if (VERB(AUTHKEY)) verb(AUTHKEY,"read_authkeys_file: can't open %s: %s\n",file,strerror(errno)); return(0); } rv = 0; in_parens = 0; ak_init(&ak); while (1) { if (fscanf(f,"%64s",&kname[0]) != 1) { fclose(f); ak_done(&ak); if (VERB(AUTHKEY)) verb(AUTHKEY,"read_authkeys_file: keyword fscanf failed, returning %d\n",rv); return(rv); } if (VERB(AUTHKEY)) verb(AUTHKEY,"read_authkeys_file: keyword is %s\n",&kname[0]); if ((kname[0] == '#') || (kname[0] == ';')) { if (VERB(AUTHKEY)) verb(AUTHKEY,"read_authkeys_file: comment line\n"); skip_to_nl(f); continue; } if (!in_parens && !strcmp(&kname[0],"(")) { if (VERB(AUTHKEY)) verb(AUTHKEY,"read_authkeys_file: starting parenthesized clause\n"); in_parens = 1; skip_to_nl(f); ak_clear(&ak); continue; } if (in_parens && !strcmp(&kname[0],")")) { if (VERB(AUTHKEY)) verb(AUTHKEY,"read_authkeys_file: finishing parenthesized clause\n"); in_parens = 0; skip_to_nl(f); if (do_callback()) break; continue; } if (in_parens) { if (VERB(AUTHKEY)) verb(AUTHKEY,"read_authkeys_file: inside parenthesized clause\n"); if (! strcmp(&kname[0],"key")) { ak_key_line(&ak,0,f); } else if (! strcmp(&kname[0],"clientip")) { ak_clientip_line(&ak,f); } else if (! strcmp(&kname[0],"command")) { ak_command_line(&ak,f); } else if (! strcmp(&kname[0],"override")) { ak_override_line(&ak,f); } else { if (VERB(AUTHKEY)) verb(AUTHKEY,"read_authkeys_file: unrecognized clause keyword `%s'\n",&kname[0]); ak.flags |= AKF_ERROR; skip_to_nl(f); continue; } } else { ak_clear(&ak); ak_key_line(&ak,&kname[0],f); if (ak.nkeys && do_callback()) break; } } fclose(f); ak_done(&ak); return(rv); } int ak_nkeys(AK *ak) { return(ak->nkeys); } const AK_KEY *ak_key(AK *ak, int x) { return(&ak->keys[x]->ekey); } static int ipv4_match(const struct in_addr *ia1, const struct in_addr *ia2, int width) { unsigned int mask; if (width == 0) mask = 0U; else mask = (~0U) << (32-mask); mask &= 0xffffffffU; return(((ntohl(ia1->s_addr)^ntohl(ia2->s_addr))&mask)==0); } static int ipv6_match(const struct in6_addr *ia1, const struct in6_addr *ia2, int width) { if ( (width >= 8) && bcmp(&ia1->s6_addr[0],&ia2->s6_addr[0],width>>3) ) return(0); if ( (width & 7) && ( (ia1->s6_addr[width>>3] ^ ia2->s6_addr[width>>3]) & 0xff & (0xff00>>(width&7)) ) ) return(0); return(1); } /* * Yes, the last arg to inet_ntop is the output buffer size. Why it's * socklen_t instead of size_t I have no idea. */ static const char *sockaddr_addr_str(const void *ipsa) { static char obuf[64]; switch (((const struct sockaddr *)ipsa)->sa_family) { case AF_INET: return(inet_ntop(AF_INET,&((const struct sockaddr_in *)ipsa)->sin_addr,&obuf[0],sizeof(obuf))); break; case AF_INET6: return(inet_ntop(AF_INET6,&((const struct sockaddr_in6 *)ipsa)->sin6_addr,&obuf[0],sizeof(obuf))); break; } panic("sockaddr_addr_str impossible family"); } static const char *ip_addr_str(int af, const void *ipa) { static char obuf[64]; return(inet_ntop(af,ipa,&obuf[0],sizeof(obuf))); } static const char *ipaction_str(IPACTION a) { switch (a) { case IPACTION_ALLOW: return("allow"); break; case IPACTION_DENY: return("deny"); break; case IPACTION_IGNORE: return("ignore"); break; } return("?""?""?"); } static const char *cmdaction_str(CMDACTION a) { switch (a) { case CMDACTION_ALLOW: return("allow"); break; case CMDACTION_DENY: return("deny"); break; } return("?""?""?"); } AK_IP_OK_RV ak_ip_ok(AK *ak, const struct sockaddr *ip, int iplen) { int i; int match; AK_IP *akip; if (VERB(AUTHKEY)) verb(AUTHKEY,"ak_ip_ok: checking %s\n",sockaddr_addr_str(ip)); if (ak->nips == 0) { if (VERB(AUTHKEY)) verb(AUTHKEY,"ak_ip_ok: no clientips\n"); return(AK_IP_ALLOW); } for (i=0;inips;i++) { akip = ak->ips[i]; switch (akip->kind) { case IPKIND_DEFAULT: if (VERB(AUTHKEY)) verb(AUTHKEY,"ak_ip_ok: checking versus %s default\n",ipaction_str(akip->action)); match = 1; break; case IPKIND_v4: if (VERB(AUTHKEY)) verb(AUTHKEY,"ak_ip_ok: checking versus %s %s/%d\n",ipaction_str(akip->action),ip_addr_str(AF_INET,&akip->a4),akip->width); match = (ip->sa_family == AF_INET) && (iplen == ip->sa_len) && ipv4_match(&((const struct sockaddr_in *)ip)->sin_addr,&akip->a4,akip->width); break; case IPKIND_v6: if (VERB(AUTHKEY)) verb(AUTHKEY,"ak_ip_ok: checking versus %s %s/%d\n",ipaction_str(akip->action),ip_addr_str(AF_INET6,&akip->a6),akip->width); match = (ip->sa_family == AF_INET6) && (iplen == ip->sa_len) && ipv6_match(&((const struct sockaddr_in6 *)ip)->sin6_addr,&akip->a6,akip->width); break; default: panic("impossible AK IP kind %d",(int)akip->kind); break; } if (match) { if (VERB(AUTHKEY)) verb(AUTHKEY,"ak_ip_ok: matched, returning\n"); switch (akip->action) { case IPACTION_ALLOW: return(AK_IP_ALLOW); break; case IPACTION_DENY: return(AK_IP_DENY); break; case IPACTION_IGNORE: return(AK_IP_SKIP); break; default: panic("impossible AK IP action %d",(int)akip->action); break; } } } if (VERB(AUTHKEY)) verb(AUTHKEY,"ak_ip_ok: no IP match, ignoring\n"); return(AK_IP_SKIP); } AK_CMD_LIST *ak_cmd_list_get(AK *ak) { if (! ak->cmdlist) { if (VERB(AUTHKEY)) verb(AUTHKEY,"ak_cmd_list_get: returning nil\n"); return(0); } ak->cmdlist->refcnt ++; if (VERB(AUTHKEY)) verb(AUTHKEY,"ak_cmd_list_get: returning %p\n",(void *)ak->cmdlist); return(ak->cmdlist); } int ak_cmd_list_ok(AK_CMD_LIST *akcl, const char *cmd, int cmdlen, int pty) { int i; AK_CMD *c; int match; if (VERB(AUTHKEY)) verb(AUTHKEY,"ak_cmd_list_ok: akcl=%p cmd=%.*s pty=%d\n",(void *)akcl,cmdlen,cmd,pty); if (! akcl) { if (VERB(AUTHKEY)) verb(AUTHKEY,"ak_cmd_list_ok: akcl is nil\n"); return(1); } if (akcl->ncmds < 1) { if (VERB(AUTHKEY)) verb(AUTHKEY,"ak_cmd_list_ok: akcl has zero commands\n"); return(1); } for (i=0;incmds;i++) { c = akcl->cmds[i]; if (VERB(AUTHKEY)) { FILE *f; f = verb_fopen(VERBOSE_AUTHKEY); fprintf(f,"ak_cmd_list_ok: checking vs %s",cmdaction_str(c->action)); if (c->flags & CMDF_TTY) fprintf(f," TTY"); if (c->flags & CMDF_NOTTY) fprintf(f," NOTTY"); if (c->flags & CMDF_LITERAL) fprintf(f," LITERAL"); if (c->flags & CMDF_SHELL) fprintf(f," (SHELL)"); if (c->flags & CMDF_NEVER) fprintf(f," (NEVER)"); if (! (c->flags & CMDF_SHELL)) fprintf(f," %s",c->str); fprintf(f,"\n"); fclose(f); } if (c->flags & CMDF_NEVER) { match = 0; if (VERB(AUTHKEY)) verb(AUTHKEY,"ak_cmd_list_ok: match=0 (NEVER)\n"); } else if ( pty ? (c->flags & CMDF_NOTTY) : (c->flags & CMDF_TTY) ) { match = 0; if (VERB(AUTHKEY)) verb(AUTHKEY,"ak_cmd_list_ok: match=0 (tty)\n"); } else if (c->flags & CMDF_SHELL) { match = !cmd && (cmdlen == AKCL_SHELL); if (VERB(AUTHKEY)) verb(AUTHKEY,"ak_cmd_list_ok: match=%d (SHELL)\n",match); } else if (c->flags & CMDF_LITERAL) { match = (cmdlen == c->str_len) && !bcmp(c->str,cmd,cmdlen); if (VERB(AUTHKEY)) verb(AUTHKEY,"ak_cmd_list_ok: match=%d (LITERAL)\n",match); } else { int err; char *emsg; int emlen; if (! c->re_bin) { c->re_bin = malloc(sizeof(regex_t)); err = regcomp(c->re_bin,c->str,REG_EXTENDED|REG_NOSUB); if (err) { if (VERB(AUTHKEY)) { emlen = regerror(err,c->re_bin,0,0); emsg = malloc(emlen); regerror(err,c->re_bin,emsg,emlen); verb(AUTHKEY,"ak_cmd_list_ok: match=0 (regcomp failed %s)\n",emsg); free(emsg); } regfree(c->re_bin); free(c->re_bin); c->re_bin = 0; c->flags |= CMDF_NEVER; match = 0; } } if (c->re_bin) { regmatch_t rm; rm.rm_so = 0; rm.rm_eo = cmdlen; err = regexec(c->re_bin,cmd,0,&rm,REG_STARTEND); if (VERB(AUTHKEY)) { switch (err) { case 0: verb(AUTHKEY,"ak_cmd_list_ok: match=1 (regexec matched)\n"); break; case REG_NOMATCH: verb(AUTHKEY,"ak_cmd_list_ok: match=0 (regexec failed)\n"); break; default: emlen = regerror(err,c->re_bin,0,0); emsg = malloc(emlen); regerror(err,c->re_bin,emsg,emlen); verb(AUTHKEY,"ak_cmd_list_ok: match=0 (regexec failure %s)\n",emsg); free(emsg); break; } } match = ! err; } } if (match) { switch (c->action) { case CMDACTION_ALLOW: if (VERB(AUTHKEY)) verb(AUTHKEY,"ak_cmd_list_ok: allow match, returning 1\n"); return(1); break; case CMDACTION_DENY: if (VERB(AUTHKEY)) verb(AUTHKEY,"ak_cmd_list_ok: deny match, returning 1\n"); return(0); break; default: panic("ak_cmd_lsit_ok: bad action %d",(int)c->action); } } else { if (VERB(AUTHKEY)) verb(AUTHKEY,"ak_cmd_list_ok: no match\n"); } } if (VERB(AUTHKEY)) verb(AUTHKEY,"ak_cmd_list_ok: out of lines, returning 0\n"); return(0); } ROSTR ak_cmd_list_override(AK_CMD_LIST *akcl) { if (! akcl) { if (VERB(AUTHKEY)) verb(AUTHKEY,"ak_cmd_override: akcl=%p\n",(void *)0); return(blk_to_rostr(0,0)); } if (VERB(AUTHKEY)) { if (akcl->override.data) { verb(AUTHKEY,"ak_cmd_override: akcl=%p cmd=%.*s\n",(void *)akcl,akcl->override.len,akcl->override.data); } else { verb(AUTHKEY,"ak_cmd_override: akcl=%p no override\n",(void *)akcl); } } return(str_to_rostr(akcl->override)); } void ak_cmd_list_done(AK_CMD_LIST *akcl) { if (VERB(AUTHKEY)) verb(AUTHKEY,"ak_cmd_list_done: akcl=%p\n",(void *)akcl); akcl_deref(akcl); }