/* * OpenFWTK authentication server * Copyright (c) 2003-2007, OpenFWTK Development Group * All rights reserved. See LICENSE. * (C) 2003 ADVA Research Center * (C) 2003 Alexei Kravchuk * (C) 2003 ArkanoiD, portability modifications */ #include #include #include #include #include #include #include #include #include #include #include #include #include "firewall.h" #include "firewall2.h" #include "fwfunc.h" #include "auth.h" #include "authdb.h" static char* moduleId ATTR_UNUSED = "$Id: authsrv.c,v 1.33 2007/10/10 18:51:15 arkenoi Exp $"; #include #include #include #include #include #include #include #include #include struct sockaddr_un *su; #define UNIX_PATH_MAX sizeof((su)->sun_path) #define MAX_AGENT_NUM 500 #ifdef OpenBSD #define HAVE_getpeereid 1 #else #ifdef linux #define HAVE_SO_PEERCRED #endif #endif typedef struct { int uid; char sock_path[UNIX_PATH_MAX]; int fd; } authsrv_agent_rec; int start_authsrv_set(authsrv_agent_rec *authsrv_agents[], fd_set *w_sock_fdset); int start_authsrv(int fd, int uid); int create_socket(authsrv_agent_rec *authsrv_agents); int my_getpeereid(int fd); int authsrv_main(); #if defined(linux) || defined (SOLARIS) extern void initsetproctitle(int,char**); #endif #include "gp_list.h" #ifndef MAXINT #define MAXINT 65535 #endif struct sockaddr_un *su; #define UNIX_PATH_MAX sizeof((su)->sun_path) #define CONS_AUTH 1 #define CONS_DENY -1 #define NO_CONS 0 Cfg * proxy_get_match(char *tag, const Group gp[], char* arg[]); extern Cfg *proxy_confp; extern void getprotolist(char*,int); static char *permdenied = "Permission Denied."; static char username[AUTH_USIZ+1]; static char usercomment[MAX_STR]; static Auth userrec; static AProto *authproto; extern AProto *codetoproto(); extern AProto *nametoproto(); static Auth userrecauth; static int authenticated = 0; static int badsleep = -1; /* how long to sleep if bad */ static int interactive = 0; static int noboguschallenge = 0; static int bogusprompt(); static int f_quit(); static int f_authorize(); static int f_response(); static int f_display(); static int f_adduser(); static int f_deluser(); static int f_enauser(); static int f_passwd(); static int f_proto(); static int f_wiz(); static int f_group(); static int f_addgroup(); static int f_revoke(); static int f_help(); static int f_login(); static int f_superwiz(); static int f_rename(); static int f_list(); static int f_authoper(); typedef struct { char *cnam; char *help; int (*cfun)(); } Cmd; static Cmd ctab[] = { { "authorize", "authorize username [comment]", f_authorize }, { "authenticate", "authenticate username", f_authorize }, { "response", "response ", f_response }, { "quit", "quit", f_quit }, { "exit", "exit", f_quit }, { "display", "display username", f_display }, { "adduser", "adduser username [fullname]", f_adduser }, { "deluser", "deluser username", f_deluser }, { "enable", "enable username [onetime]", f_enauser }, { "disable", "disable username", f_enauser }, { "login", NULL, f_login }, { "password", "password [username] passwordtext", f_passwd }, { "passwd", "passwd [username] passwordtext", f_passwd }, { "proto", "proto username protoname", f_proto }, { "group", "group username groupname [{ groupname}]",f_group }, { "addgroup", "addgroup username groupname [{ groupname}]",f_addgroup }, { "revoke", "revoke username groupname", f_revoke }, { "rename", "rename username newname [fullname]", f_rename }, { "wiz", "wiz username groupname [{ groupname}]",f_wiz }, { "unwiz", "unwiz username groupname [{ groupname}]",f_wiz }, { "superwiz", "superwiz username", f_superwiz }, { "operation","operation group/user username command dest [tokens]",f_authoper}, { "list", "list [group]", f_list }, { "ls", "ls [group]", f_list }, { "?", "?", f_help }, { "help", "help", f_help }, { NULL, NULL, NULL } }; static Cmd * find_command(); const Group unknown_gp[] = {{"unknown", 0}, {"\0\0", 0}}; const Group unauth_gp[] = {{"unauth", 0}, {"\0\0", 0}}; sa_family_t get_sock_type(int fd); Cfg* authsrv_proxy_conf_hosts (Cfg *confp, char *rladdr, char *riaddr); Cfg* authsrv_init(int); void log_usrmsg (char *msg, char *prm1, char *prm2_pat, char *prm2); void showusage (char *usage); int check_auth_console(char *cons_sockname, char []); int makeup_cons_sockname(char *sockdir, char *host, char *username, char *sockname); static int time_ok(char *upper,char *lower); char *sockdir = AUTHSRV_SOCK_PATH; char console_pwd[AUTH_PWSIZ+1] = ""; int main(int argc, char *argv[]) { Cfg *authsrvd_cfp, *cf; char *sock_name; int i, maxfd = 0; authsrv_agent_rec *authsrv_agents[MAX_AGENT_NUM+1]; fd_set r_sock_fdset, sock_fdset; #ifndef LOG_DAEMON openlog("authsrv",LOG_PID); #else openlog("authsrv",LOG_PID|LOG_NDELAY,LFAC); #endif #if defined(linux) || defined (SOLARIS) initsetproctitle(argc, argv); #endif interactive = isatty(0); switch (argc) { case 2: if (!strcmp(argv[1],"-daemon")) { interactive = 0; break; } else if (strcmp(argv[1],"-s")) { fprintf(stderr,"usage: %s [-daemon]\n", basename(argv[0])); exit(EX_USAGE); } else interactive = 1; case 1: if (interactive && (getuid() != 0)) { syslog(LLEV,"securityalert: non-root user started authsrv in interactive mode"); fprintf(stderr,"Only root is allowed to use administration mode\n"); exit(1); } authsrv_main(); default:fprintf(stderr,"usage: %s [-daemon]\n", basename(argv[0])); exit(EX_USAGE); } FD_ZERO(&sock_fdset); if((authsrvd_cfp = cfg_read("authsrv")) == (Cfg *)-1) { fprintf(stderr,"Error: cannot read configuration file\n"); syslog(LLEV,"fwtkcfgerr: no netperm-table records for authsrvd"); exit(1); } sock_name = proxy_conf_string(authsrvd_cfp, "listen-socket"); i=0; for (cf = cfg_get("listen-socket", authsrvd_cfp); cf != (Cfg*) 0; cf = cfg_get("listen-socket", (Cfg*) 0)) { if (i >= MAX_AGENT_NUM) { fprintf(stderr,"Error: too many sockets to listen\n"); syslog(LLEV,"fwtkcfgerr: too many sockets to listen"); exit(1); } authsrv_agents[i] = (authsrv_agent_rec *)malloc(sizeof(authsrv_agent_rec)); strlcpy(authsrv_agents[i]->sock_path, cf->argv[0], sizeof(authsrv_agents[i]->sock_path)); if ((cf->argc < 1) || (cf->argv[0][0] == '-')) { fprintf(stderr,"Configuration error\n"); syslog(LLEV,"fwtkcfgerr: missing parameter, line %d",cf->ln); exit(1); } authsrv_agents[i]->uid = -1; if (cf->argc == 3) { if (!strcmp(cf->argv[1],"-user")) { if ((authsrv_agents[i]->uid = mapuid(cf->argv[2])) == -1) { fprintf(stderr,"Configuration error\n"); syslog(LLEV,"fwtkcfgerr: invalid user id %.128s, line %d",cf->argv[2],cf->ln); exit(1); } } else { fprintf(stderr,"Configuration error\n"); syslog(LLEV,"fwtkcfgerr: invalid parameter %.128s, line %d",cf->argv[2],cf->ln); exit(1); } } else if (cf->argc != 1) { fprintf(stderr,"Configuration error\n"); syslog(LLEV,"fwtkcfgerr: syntax error, line %d",cf->ln); exit(1); } authsrv_agents[i]->fd = create_socket(authsrv_agents[i]); FD_SET(authsrv_agents[i]->fd, &sock_fdset); maxfd = (authsrv_agents[i]->fd > maxfd) ? authsrv_agents[i]->fd : maxfd; i++; } switch(fork()) { case -1: syslog(LLEV,"fwtksyserr: fork failed"); fprintf(stderr,"Cannot fork!\n"); exit(1); case 0: break; default: exit(0); } cfg_free(authsrvd_cfp); authsrv_agents[i] = NULL; r_sock_fdset = sock_fdset; /* The cycle is never-ending actually */ while((i = select(maxfd + 1, &r_sock_fdset, NULL, NULL, NULL))) { start_authsrv_set(authsrv_agents, &r_sock_fdset); r_sock_fdset = sock_fdset; } /* Free all agent records before exiting */ for(i = 0; authsrv_agents[i]; i++) { free(authsrv_agents[i]); } return 0; } int create_socket(authsrv_agent_rec *authsrv_agent) { int sock; char buf[MAX_STR] = ""; char *p,*p1; umask(0177); strtrm(authsrv_agent->sock_path); p = xstrdup(authsrv_agent->sock_path); if ((p1 = strchr(p, ':'))) { *(p1++) = '\0'; if (strcasecmp(p, "unix") == 0 || strcasecmp(p, "local") == 0) { struct sockaddr_un sn; memset(&sn,0,sizeof(sn)); sn.sun_family = AF_UNIX; sock = socket(PF_UNIX, SOCK_STREAM, 0); if (sock < 0) { perror("socket"); syslog(LOG_ERR, "fwtksyserr: can't create UNIX socket: %s", strerror(errno)); exit(1); } strlcpy(sn.sun_path, p1, sizeof(sn.sun_path)); unlink(sn.sun_path); if (bind(sock, (struct sockaddr *)&sn, sizeof(sn)) < 0) { perror("bind"); syslog(LOG_ERR, "fwtksyserr: can't bind UNIX socket %.128s: %s",sn.sun_path,strerror(errno)); exit(1); } chown(sn.sun_path, authsrv_agent->uid, -1); snprintf(buf, sizeof(buf), "(for uid: %d)", authsrv_agent->uid); } else if (strcasecmp(p, "inet") == 0) { struct sockaddr_in si; char *bind_addr; if (authsrv_agent->uid != -1) { syslog(LOG_ERR, "fwtkcfgerr: cannot determine remote user id for inet sockets"); exit(1); } memset(&si,0,sizeof(si)); si.sin_family = AF_INET; if ((bind_addr = strchr(p1,'@'))) { *(bind_addr++) = '\0'; si.sin_addr.s_addr = inet_addr(bind_addr); } si.sin_port = htons(str_to_port(p1)); sock = socket(PF_INET, SOCK_STREAM, 0); if (sock < 0) { perror("socket"); syslog(LOG_ERR, "fwtksyserr: can't create INET socket: %s", strerror(errno)); exit(1); } if (bind(sock, (struct sockaddr *)&si, sizeof(si)) < 0) { perror("bind"); syslog(LOG_ERR, "fwtksyserr: can't bind INET port %.128s: %s",p1,strerror(errno)); exit(1); } } else { syslog(LOG_ERR, "fwtkcfgerr: unsupported socket type %.128s",p); exit(1); } } else { syslog(LOG_ERR, "fwtkcfgerr: socket definition sytax error %.128s",p); exit(1); } free(p); if (listen(sock, 1) < 0) { perror("listen"); syslog(LOG_ERR, "fwtksyserr: error while listening to the socket: %s", strerror(errno)); return -1; } syslog(LLEV, "listening at %s%s", authsrv_agent->sock_path, (authsrv_agent->uid != -1) ? buf : ""); return sock; } int start_authsrv_set(authsrv_agent_rec *authsrv_agents[], fd_set *w_sock_fdset) { int i, num_of_started = 0; for(i=0; authsrv_agents[i]; i++) { if(FD_ISSET(authsrv_agents[i]->fd, w_sock_fdset)) { if(start_authsrv(authsrv_agents[i]->fd, authsrv_agents[i]->uid) < 0) { syslog(LLEV, "fwtksyserr: failed to start authsrv on the %.128s socket", authsrv_agents[i]->sock_path); } num_of_started++; } } return num_of_started; } int start_authsrv(int fd, int uid) { int ch_pid; int authsrv_fd; authsrv_fd = accept(fd, NULL, NULL); if( uid != -1 ) { if(my_getpeereid(authsrv_fd) != uid #ifdef ALLOW_ROOT && my_getpeereid(authsrv_fd) != 0 #endif ) { syslog(LLEV, "securityalert: peer eid verification failed, can't start authsrv,"); close(authsrv_fd); return -1; } } ch_pid = fork(); if(ch_pid < 0) { /* error */ perror("fork"); syslog(LLEV, "fwtksyserr: fork(): %s", strerror(errno)); exit(1); } if(ch_pid) { /* parent */ signal(SIGCHLD,SIG_IGN); close(authsrv_fd); } else { /* child */ if (close(fd) || close(0) || close(1) || close(2)) { syslog(LLEV, "fwtksyserr: close(): %s", strerror(errno)); exit(1); } if ((dup2(authsrv_fd, 0) == -1) || (dup2(authsrv_fd, 1) == -1) || (dup2(authsrv_fd, 2) == -1)) { syslog(LLEV, "fwtksyserr: dup2(): %s", strerror(errno)); exit(1); } if(close(authsrv_fd)) { syslog(LLEV, "fwtksyserr: close(): %s", strerror(errno)); exit(1); } authsrv_main(); exit(1); /* reach here only if error occured */ } return 1; } int my_getpeereid(int fd) { #ifdef HAVE_getpeereid uid_t euid; gid_t egid; if(getpeereid(fd, &euid, &egid) == -1) return -1; /* error */ return euid; #elif defined(HAVE_getpeeruid) uid_t uid; gid_t gid; if(getpeeruid(fd, &uid, &gid) == -1) return -1; /* error */ return uid; #elif defined(HAVE_SO_PEERCRED) struct ucred ucred; socklen_t optlen; optlen = sizeof(ucred); if(getsockopt(fd, SOL_SOCKET, SO_PEERCRED, (void *) &ucred, &optlen) == -1) { return -1; } return ucred.uid; #endif return -1; } int authsrv_main() { Cmd *cp; char buf[MAX_STR]; char *xa[MAX_ARG]; char xb[MAX_STR]; int xc; char *p; int promptlen; char *prompt; sosetline(SO_LLF); username[0] = '\0'; prompt = "authsrv-> "; promptlen = strlen(prompt); authsrv_init(interactive); /* if invoked by root from a tty go into admin mode */ if (interactive) { authenticated = 1; strlcpy(username,"administrator",sizeof(username)); userrecauth.gp[0].name[0] = '\0'; userrecauth.gp[0].name[1] = '\0'; userrecauth.bcnt = 0; userrecauth.flgs = 0 | AUTHFLG_WIZ; userrecauth.last = (long)0; userrecauth.lastpw = (long)0; userrecauth.atyp = 'p'; userrecauth.pw[0] = '*'; userrecauth.pw[1] = '\0'; prompt = "authsrv# "; promptlen = strlen(prompt); } if(auth_dbconfig(proxy_confp)) { if(interactive) fprintf(stderr,"Database not configured"); exit(1); } if(!interactive) { if(get_sock_type(0) == AF_INET && authsrv_proxy_conf_hosts(proxy_confp,proxy_stats.rladdr,proxy_stats.riaddr) == NULL) { sosay(0,"Access denied"); exit(0); } snprintf(buf,sizeof(buf),"Authsrv ready. (%s)",FWTK_VERSION_MINOR); sosay(0, buf); } proxy_setugid(); badsleep = proxy_conf_int(proxy_confp, "badsleep", -1, MAXINT, -1); if(proxy_conf_string(proxy_confp, "nobogus")) noboguschallenge++; while(1) { if(interactive) write(1,prompt,promptlen); if(sogets(0,buf,sizeof(buf)) < 1) break; if((p = rindex(buf,'\n')) != (char *)0) *p = '\0'; xc = enargv(buf,xa,sizeof(xa)/sizeof(char *),xb,sizeof(xb)); if(xc < 0) { sosay(0, "Too many command parameters."); continue; } if(xc == 0) continue; cp = find_command(ctab, xa[0]); if (!cp) { char xuf[MAX_STR]; syslog(LLEV,"ambiguous command: %.200s",xa[0]); snprintf(xuf,sizeof(xuf),"Command \"%.200s\" is ambiguous.",xa[0]); sosay(0, xuf); continue; } if(cp->cnam == (char *)0) { char xuf[MAX_STR]; syslog(LLEV,"unrecognized command: %.200s",xa[0]); snprintf(xuf,sizeof(xuf),"Command \"%.200s\" unrecognized.",xa[0]); sosay(0, xuf); continue; } (*cp->cfun)(xc,xa,buf,cp->help); } if(interactive) write(1,"\nEOT\n",5); exit (0); } static Cmd * find_command (tab, cmd) Cmd * tab; char *cmd; { Cmd *cp, *match; int nl; nl = strlen(cmd); match = 0; for (cp = tab; cp->cnam != (char *)0; cp++) { if (!strcmp(cp->cnam, cmd)) return cp; else if (!strncasecmp(cp->cnam, cmd, nl)) { if (match && cp->cfun != match->cfun) return 0; /* ambiguous */ match = cp; } } return match ? match : cp; } static int f_quit(ac,av,orig, usage) int ac; char *av[]; char *orig; char *usage; { exit(0); return 0; } /* Convert hh:mm to munutes since midnight */ static int minutes(p) char *p; { #define D(x) isdigit((unsigned char) x) if (!D(p[0]) || !D(p[1]) || p[2] != ':' || !D(p[3]) || !D(p[4]) || p[5] != '\0') { syslog(LLEV,"fwtkcfgerr: invalid time specification: %.10s",p); return -1; } return((p[0] - '0') * 600 + (p[1] - '0') * 60 + (p[3] - '0') * 10 + (p[4] - '0')); } /* * Return 0 if current time is between upper and lower (inclusive) */ static int time_ok(upper,lower) char *upper; char *lower; { int up; int low; int present; time_t time_sec; time(&time_sec); present = minutes(ctime(&time_sec) + 11); up = minutes(upper); low = minutes(lower); if (present < 0 || up < 0 || low < 0) return 1; /* invalid time */ if (up < low) return (low <= present || present <= up) ? 0 : 1; else return (low <= present && present <= up) ? 0 : 1; } static int f_authorize(ac,av,orig,usage) int ac; char *av[]; char *orig; char *usage; { Cfg *cfp; char buf[MAX_STR]; char proxy_name[MAX_STR]; char ciaddr[MAX_HOSTNAME]; char *p; char* args[3] = {av[1], proxy_name, ciaddr}; char cons_sockname[UNIX_PATH_MAX]; int cons_resp; username[0] = '\0'; authenticated = 0; console_pwd[0] = '\0'; /* Clean up console-generated session pwd for the new auth context */ if(ac < 2) { showusage(usage); return(1); } usercomment[0] = '\0'; if(ac > 2) { strlcpy(usercomment,av[2],sizeof(usercomment)); if ((p = rindex(usercomment,'/')) != (char *)0) strlcpy(ciaddr,p+1,sizeof(ciaddr)); else strlcpy(ciaddr,"unknown",sizeof(ciaddr)); strlcpy(proxy_name,usercomment,sizeof(proxy_name)); if ((p = index(proxy_name,' ')) != (char *)0) *p= '\0'; } else { strlcpy(proxy_name,"unknown",sizeof(proxy_name)); strlcpy(ciaddr,"unknown",sizeof(ciaddr)); } strlcpy(username,av[1],sizeof(username)); /* if there is no record for the guy, send a dummy challenge */ if(auth_dbgetu(username,&userrec)) { log_usrmsg("BADAUTH %.100s",username," (%.512s)", usercomment); username[0] = '\0'; if(noboguschallenge) return(sosay(0, "Userid nonexistent")); return(bogusprompt()); } console_pwd[0] = '\0'; cons_resp = NO_CONS; if(makeup_cons_sockname(sockdir, ciaddr, username, cons_sockname) && (cons_resp = check_auth_console(cons_sockname, orig)) == CONS_AUTH) { authenticated = 1; if(auth_dbgetu_forupdate(username,&userrec)) { /* * User mysteriously vanished while we checked the console! */ log_usrmsg("BADAUTH %.100s",username," (%.512s)", usercomment); username[0] = '\0'; if(noboguschallenge) return(sosay(0, "Userid nonexistent")); return(bogusprompt()); } bcopy(&userrec,&userrecauth,sizeof(Auth)); /* update last login */ time((time_t*) &(userrecauth.last)); if(strlen(console_pwd) > 0) { authenticated = 0; /* Not authenticated yet. Should enter console-generated session pwd before */ sosay(0, "password"); } else { (void)auth_dbputu(username,&userrecauth); log_usrmsg("AUTHENTICATE %.100s",username," (%.512s)",usercomment); sosay(0, "ok"); } auth_dbclose(); /* Unlock and close DB */ return(0); } else if(cons_resp == CONS_DENY) { log_usrmsg("BADAUTH %.100s authentication denied via auth console",username, " (%.512s)", usercomment); username[0] = '\0'; auth_dbclose(); /* Unlock and close DB */ return(sosay(0, "Authentication denied via auth console")); } auth_dbclose(); /* Unlock and close DB */ cfp = proxy_get_match("authentication", userrec.gp, args); if((cfp == (Cfg *)0) || (cfp->flags & PERM_DENY)) { log_usrmsg("BADAUTH %.100s authentication denied",username, " (%.512s)", usercomment); username[0] = '\0'; if(noboguschallenge) return(sosay(0, "Authentication denied")); return(bogusprompt()); } /* if the login is sleeping from bad logins */ if(userrec.flgs & AUTHFLG_BSLP) { time_t now; time(&now); if(badsleep == -1 || now - userrec.last < badsleep) { log_usrmsg("BADAUTH %.100s too many tries",username, " (%.512s)",usercomment); username[0] = '\0'; if(noboguschallenge && badsleep != -1) return(sosay(0, "Userid temporarily disabled")); if(noboguschallenge) return(sosay(0, "Userid disabled")); return(bogusprompt()); } else userrec.flgs &= ~AUTHFLG_BSLP; } /* if the login is disabled */ if(userrec.flgs & AUTHFLG_DIS) { log_usrmsg("BADAUTH %.100s is disabled",username," (%.512s)",usercomment); username[0] = '\0'; if(noboguschallenge) return(sosay(0, "Userid disabled")); return(bogusprompt()); } /* figure out what authentication protocol this user uses */ if((authproto = codetoproto(userrec.atyp)) == (AProto *)0) { syslog(LLEV,"fwtkcfgerr: %.100s: bad auth protocol %c",username,userrec.atyp); authenticated = 0; if(noboguschallenge) return(sosay(0, "Userid authentication protocol invalid")); return(bogusprompt()); } /* if there is no challenge generator for this protocol just ask for a password. */ if(authproto->challf == 0) { sosay(0, "password"); return(0); } /* ok, there IS a challenge generator - call it */ strlcpy(buf,"challenge ",sizeof(buf)); if((*authproto->challf)(username,&buf[10],sizeof(buf) - 10) != 0) { username[0] = '\0'; if(noboguschallenge) return(sosay(0, &buf[10])); return(bogusprompt()); } sosay(0, buf); return(0); } static int f_authoper(ac,av,orig,usage) int ac; char *av[]; char *orig; char *usage; { Cfg *cfp; int counter; const Group* gp; if(ac < 5 || (strcmp(av[1], "user") != 0 && strcmp(av[1], "group") != 0)){ showusage(usage); return(1); } if(strcmp(av[2], "unauth") == 0) { gp = unauth_gp; } else if (auth_dbgetu(av[2], &userrec)) { cfp = cfg_get("unknown", proxy_confp); if ((cfp == (Cfg *)0 || (cfp->flags & PERM_DENY)) ) { syslog(LLEV,"fwtkcfgerr: authsrv asked to verify user %.100s who does not exist?", av[2]); sosay(0, "User not found in auth DB"); return (0); } /* Make sure the unknown user is specified. */ for(counter = 0; counter < cfp->argc; counter++){ if( nacasematch(cfp->argv[counter], av[2])) break; } if( counter == cfp->argc) { sosay(0, permdenied); return (0); } gp = unknown_gp; } else gp = userrec.gp; cfp = proxy_get_match("operation", gp, &av[2]); if(cfp == (Cfg *)0) { sosay(0, "No match in netperm-table"); return(0); } if((cfp->flags & PERM_DENY) == 0) { sosay(0, "ok "); return (0); } sosay(0, permdenied); return(0); } static int f_help(ac,av,orig,usage) int ac; char *av[]; char *orig; char *usage; { Cmd *cp; sosay(0, "Command List:"); sosay(0, "(Commands may be invoked with unique leading abbreviation)"); for(cp = ctab; cp->cnam != (char *)0; cp++) if(cp->help != (char *)0) sosay(0, cp->help); return(0); } static int f_login(ac,av,orig,usage) int ac; char *av[]; char *orig; char *usage; { sosay(0, "Login not supported directly in authsrv."); sosay(0, "Use 'authorize' and 'response'"); return(0); } static int f_response(ac,av,orig,usage) int ac; char *av[]; char *orig; char *usage; { char rbuf[MAX_STR]; int authresponse; /* this guy is skanking us */ if(authenticated || username[0] == '\0') { username[0] = '\0'; authenticated = 0; sosay(0, permdenied); return(0); } if(ac != 2) av[1] = ""; /* Re-read a user record and lock DB */ if(auth_dbgetu_forupdate(username,&userrec)) { /* DB remains locked to avoid a race condition */ log_usrmsg("BADAUTH %.100s",username," (%.512s)", usercomment); username[0] = '\0'; return(sosay(0, "Userid nonexistent")); } if(strlen(console_pwd) > 0) { if((authproto = codetoproto('p')) == (AProto *)0) { syslog(LLEV,"fwtkcfgerr: %.100s: can't set auth protocol 'p'",username); authenticated = 0; auth_dbclose(); /* Unlock and close DB */ return(sosay(0, "Can't set 'password' auth protocol")); } } /* figure out what authentication protocol this user uses */ else if(authproto != codetoproto(userrec.atyp)) { syslog(LLEV,"fwtkcfgerr: %.100s: auth protocol %c differs with the one used for auth request %c",username,userrec.atyp, authproto->typ); authenticated = 0; auth_dbclose(); /* Unlock and close DB */ return(sosay(0, "Userid authentication protocol has been changed since auth was requested. Please re-request auth.")); } if(authproto != (AProto *)0 && authproto->verf == 0) { strlcpy(rbuf,"ok",sizeof(rbuf)); goto ok_login; } if(authproto == (AProto *)0) { authresponse = 1; } else { authresponse = (*authproto->verf)(username,av[1],&userrec,rbuf); } if (authresponse == 2) { sosay(0, rbuf); auth_dbclose(); /* Unlock and close DB */ return(0); } if (authresponse) { log_usrmsg("BADAUTH %.100s",username," (%.512s)",usercomment); /* the guy bombed out. :( */ userrec.bcnt++; time((time_t*)&(userrec.last)); /* set the guy to have a login delay */ if((userrec.bcnt >= AUTH_MAXBAD) && ((userrec.flgs & (AUTHFLG_WIZ | AUTHFLG_GWIZ)) == 0)) { userrec.flgs |= AUTHFLG_BSLP; log_usrmsg("securityalert: repeated bad auth attempts %.100s",username," (%.512s)",usercomment); } (void)auth_dbputu(username,&userrec); username[0] = '\0'; authenticated = 0; if(authproto == (AProto *)0) sosay(0, permdenied); else sosay(0, rbuf); auth_dbclose(); /* Unlock and close DB */ return(0); } ok_login: authenticated = 1; bcopy(&userrec,&userrecauth,sizeof(Auth)); /* update last login */ time((time_t*)&(userrecauth.last)); userrecauth.bcnt = 0; userrecauth.flgs &= ~AUTHFLG_BSLP; if(userrecauth.flgs & AUTHFLG_ONETIME) { syslog(LLEV,"ONE-TIME %.100s will be disabled",username); userrecauth.flgs |= AUTHFLG_DIS; } (void)auth_dbputu(username,&userrecauth); auth_dbclose(); /* Unlock and close DB */ log_usrmsg("AUTHENTICATE %.100s",username," (%.512s)",usercomment); sosay(0, rbuf); return(0); } static int f_adduser(ac,av,orig,usage) int ac; char *av[]; char *orig; char *usage; { int gp_num, usr_gp_num; if(!authenticated) { sosay(0, permdenied); return(0); } if((userrecauth.flgs & AUTHFLG_WIZ) == 0 && (userrecauth.flgs & AUTHFLG_GWIZ) == 0) { sosay(0, permdenied); return(0); } if(ac < 2) { showusage(usage); return(0); } if(auth_dbgetu_forupdate(av[1],&userrec) == 0) { /* DB remains locked to avoid a race condition */ sosay(0, "User already exists."); return(0); } userrec.ln[0] = '\0'; if(ac == 3) strlcpy(userrec.ln,av[2],AUTH_LNSIZ); userrec.pw[0] = '*'; userrec.pw[1] = '\0'; /* inherit authentication method from creator */ userrec.atyp = userrecauth.atyp; userrec.bcnt = 0; userrec.flgs = 0 | AUTHFLG_DIS; userrec.last = (long)0; userrec.authproto_rec.active = 0 ; /* Initially disable authentication until it has been initialized */ memset(userrec.authproto_rec.buf, 0, AUTH_PROTO_BUF_LEN); for(gp_num=0; gp_num < AUTH_GNUM; gp_num++) { userrec.gp[gp_num].name[0] = '\0'; userrec.gp[gp_num].name[1] = '\0'; userrec.gp[gp_num].flgs = 0; } /* if created by a group wizard, become a member of the group */ for(usr_gp_num=0, gp_num=0; usr_gp_num < AUTH_GNUM; usr_gp_num++) { if(end_gp_list(userrecauth.gp,usr_gp_num)) break; if(userrecauth.gp[usr_gp_num].flgs & AUTHFLG_GWIZ) { userrec.gp[gp_num].flgs = 0; strlcpy(userrec.gp[gp_num++].name, userrecauth.gp[usr_gp_num].name, AUTH_GSIZ); } } if(auth_dbputu(av[1],&userrec)) { sosay(0, "database error"); auth_dbclose(); /* Unlock and close DB */ return(0); } syslog(LLEV,"%.100s ADDED USER %.100s",username,av[1]); sosay(0, "ok - user added initially disabled"); auth_dbclose(); /* Unlock and close DB */ return(0); } static int f_rename(ac,av,orig,usage) int ac; char *av[]; char *orig; char *usage; { if(!authenticated) { sosay(0, permdenied); return(0); } if((userrecauth.flgs & AUTHFLG_WIZ) == 0 && (userrecauth.flgs & AUTHFLG_GWIZ) == 0) { sosay(0, permdenied); return(0); } if(ac < 3) { showusage(usage); return(0); } if(auth_dbgetu(av[2],&userrec) == 0) { char xuf[MAX_STR]; snprintf(xuf,sizeof(xuf),"User %.100s already exists.",av[2]); sosay(0, xuf); return(0); } if(auth_dbgetu_forupdate(av[1],&userrec) != 0) { /* DB remains locked to avoid a race condition */ char xuf[MAX_STR]; snprintf(xuf,sizeof(xuf),"User %.100s does not exist.",av[1]); sosay(0, xuf); return(0); } /* you can only do it if you're in that group or a wizard */ if((userrecauth.flgs & AUTHFLG_WIZ) == 0 && ((userrecauth.flgs & AUTHFLG_GWIZ) == 0 || !check_gwiz(userrecauth.gp,userrec.gp))) { sosay(0, permdenied); auth_dbclose(); /* Unlock and close DB */ return(0); } if(ac >= 4) strlcpy(userrec.ln,av[3],AUTH_LNSIZ); if(auth_dbopen() || auth_dbputu(av[2],&userrec) || auth_dbdelu(av[1]) || auth_dbclose()) { sosay(0, "database error"); auth_dbclose(); /* Unlock and close DB */ return(0); } syslog(LLEV,"%.100s RENAMED USER %.100s to %.100s",username,av[1],av[2]); sosay(0, "ok - user renamed"); auth_dbclose(); /* Unlock and close DB */ return(0); } static int f_deluser(ac,av,orig,usage) int ac; char *av[]; char *orig; char *usage; { if(!authenticated) { sosay(0, permdenied); return(0); } if((userrecauth.flgs & AUTHFLG_WIZ) == 0 && (userrecauth.flgs & AUTHFLG_GWIZ) == 0) { sosay(0, permdenied); return(0); } if(ac != 2) { showusage(usage); return(0); } if(auth_dbgetu_forupdate(av[1],&userrec)) { /* DB remains locked to avoid a race condition */ auth_dbclose(); /* Unlock and close DB */ sosay(0, "Unknown user"); return(0); } /* if a group wizard, can delete a member of the group */ if((userrecauth.flgs & AUTHFLG_WIZ) == 0 && ((userrecauth.flgs & AUTHFLG_GWIZ) == 0 || !check_gwiz(userrecauth.gp,userrec.gp))) { sosay(0, permdenied); auth_dbclose(); /* Unlock and close DB */ return(0); } if(auth_dbdelu(av[1])) { sosay(0, "database error"); auth_dbclose(); /* Unlock and close DB */ return(0); } syslog(LLEV,"%.100s DELETED USER %.100s",username,av[1]); sosay(0, "deleted"); auth_dbclose(); /* Unlock and close DB */ return(0); } static int f_enauser(ac,av,orig,usage) int ac; char *av[]; char *orig; char *usage; { int onetime = 0; if(!authenticated) { sosay(0, permdenied); return(0); } if((userrecauth.flgs & AUTHFLG_WIZ) == 0 && (userrecauth.flgs & AUTHFLG_GWIZ) == 0) { sosay(0, permdenied); return(0); } if(ac < 2) { showusage(usage); return(0); } if(ac == 3 && !strncmp(av[2],"onetime",strlen(av[2]))) onetime = 1; else if(ac > 2) { sosay(0, "Unrecognized command parameter"); return(0); } if(auth_dbgetu_forupdate(av[1],&userrec)) { /* DB remains locked to avoid a race condition */ sosay(0, "Unknown user"); return(0); } /* if a group wizard, can enable/disable a member of the group */ if((userrecauth.flgs & AUTHFLG_WIZ) == 0 && ((userrecauth.flgs & AUTHFLG_GWIZ) == 0 || !check_gwiz(userrecauth.gp,userrec.gp))) { sosay(0, permdenied); auth_dbclose(); /* Unlock and close DB */ return(0); } if(av[0][0] == 'e' || av[0][0] == 'E') { userrec.flgs &= ~(AUTHFLG_DIS | AUTHFLG_BSLP | AUTHFLG_ONETIME); if(onetime) userrec.flgs |= AUTHFLG_ONETIME; userrec.bcnt = 0; if(auth_dbputu(av[1],&userrec)) { sosay(0, "database error"); auth_dbclose(); /* Unlock and close DB */ return(0); } syslog(LLEV,"%.100s ENABLED USER %.100s",username,av[1]); sosay(0, "enabled"); auth_dbclose(); /* Unlock and close DB */ return(0); } userrec.flgs |= AUTHFLG_DIS; userrec.bcnt = 0; if(auth_dbputu(av[1],&userrec)) { sosay(0, "database error"); auth_dbclose(); /* Unlock and close DB */ return(0); } syslog(LLEV,"%.100s DISABLED USER %.100s",username,av[1]); sosay(0, "disabled"); auth_dbclose(); /* Unlock and close DB */ return(0); } static int f_wiz(ac,av,orig,usage) int ac; char *av[]; char *orig; char *usage; { int gwiz_gp_num, gp_num; char gp_buf[AUTH_GSIZ*AUTH_GNUM + 1]; char cbuf[MAX_STR]; if(!authenticated) { sosay(0, permdenied); return(0); } if((userrecauth.flgs & AUTHFLG_WIZ) == 0) { sosay(0, permdenied); return(0); } if(ac < 3) { showusage(usage); return(0); } if(auth_dbgetu_forupdate(av[1],&userrec)) { /* DB remains locked to avoid a race condition */ sosay(0, "Unknown user"); return(0); } for(gwiz_gp_num=2; gwiz_gp_num < ac; gwiz_gp_num++) { if((gp_num = strcmplist(av[gwiz_gp_num], userrec.gp)) == -1) { snprintf(cbuf, sizeof(cbuf), "User %.100s is not a member of the %.100s group", av[1], av[gwiz_gp_num]); syslog(LLEV, "wiz: User %.100s is not a member of the %.100s group", av[1], av[gwiz_gp_num]); sosay(0, cbuf); auth_dbclose(); /* Unlock and close DB */ return 0; } if (strcmp(av[gwiz_gp_num],userrec.gp[gp_num].name)) continue; if(av[0][0] == 'u' || av[0][0] == 'U') userrec.gp[gp_num].flgs &= ~AUTHFLG_GWIZ; else { userrec.gp[gp_num].flgs |= AUTHFLG_GWIZ; userrec.flgs |= AUTHFLG_GWIZ; } strlcat(gp_buf, av[gwiz_gp_num], sizeof(gp_buf)); strlcat(gp_buf, " ", sizeof(gp_buf)); } if(av[0][0] == 'u' || av[0][0] == 'U') { if(!check_gwiz(userrec.gp, userrec.gp)) userrec.flgs &= ~AUTHFLG_GWIZ; if(auth_dbputu(av[1],&userrec)) { sosay(0, "database error"); auth_dbclose(); /* Unlock and close DB */ return(0); } syslog(LLEV,"%.100s UN-GWIZ USER %s for GROUPS %s",username,av[1], gp_buf); sosay(0, "unset group-wizard"); auth_dbclose(); /* Unlock and close DB */ return(0); } if(auth_dbputu(av[1],&userrec)) { sosay(0, "database error"); auth_dbclose(); /* Unlock and close DB */ return(0); } syslog(LLEV,"%.100s GWIZ USER %.100s for GROUPS %s",username,av[1], gp_buf); sosay(0, "set group-wizard"); auth_dbclose(); /* Unlock and close DB */ return(0); } static int f_superwiz(ac,av,orig,usage) int ac; char *av[]; char *orig; char *usage; { if(!authenticated) { sosay(0, permdenied); return(0); } if((userrecauth.flgs & AUTHFLG_WIZ) == 0) { sosay(0, permdenied); return(0); } if(!interactive || !isatty(0)) { sosay(0, "Must be logged in as root on terminal."); return(0); } if(ac != 2) { showusage(usage); return(0); } if(auth_dbgetu_forupdate(av[1],&userrec)) { /* DB remains locked to avoid a race condition */ sosay(0, "Unknown user"); return(0); } userrec.flgs |= AUTHFLG_WIZ; if(auth_dbputu(av[1],&userrec)) { sosay(0, "database error"); auth_dbclose(); /* Unlock and close DB */ return(0); } syslog(LLEV,"%.100s WIZ USER %.100s",username,av[1]); sosay(0, "set wizard"); auth_dbclose(); /* Unlock and close DB */ return(0); } static int f_group(ac,av,orig,usage) int ac; char *av[]; char *orig; char *usage; { int gp_num, arg_gp_num; char gp_buf[(AUTH_GSIZ+2) * (AUTH_GNUM + 1)] = ""; char cbuf[MAX_STR]; if(!authenticated) { sosay(0, permdenied); return(0); } if((userrecauth.flgs & AUTHFLG_WIZ) == 0) { sosay(0, permdenied); return(0); } if(ac < 3) { showusage(usage); return(0); } if(auth_dbgetu_forupdate(av[1],&userrec)) { /* DB remains locked to avoid a race condition */ sosay(0, "Unknown user"); return(0); } for(gp_num=0; gp_num < AUTH_GNUM; gp_num++) { userrec.gp[gp_num].name[0] = '\0'; userrec.gp[gp_num].name[1] = '\0'; /* wipe out */ userrec.gp[gp_num].flgs = 0; } userrec.flgs &= ~AUTHFLG_GWIZ; gp_num = ac - 2; if(gp_num >= AUTH_GNUM) { snprintf(cbuf, sizeof(cbuf), "Max number of groups reached for user %s. Can't assign group %s", av[1], av[AUTH_GNUM + 2]); syslog(LLEV, cbuf); sosay(0, cbuf); auth_dbclose(); /* Unlock and close DB */ return(0); } for(arg_gp_num = 2, gp_num = 0; arg_gp_num < ac; arg_gp_num++, gp_num++) { strlcpy(userrec.gp[gp_num].name,av[arg_gp_num],AUTH_GSIZ); userrec.gp[gp_num].flgs = 0; strlcat(gp_buf, av[arg_gp_num], sizeof(gp_buf)); strlcat(gp_buf, " ", sizeof(gp_buf)); } if(auth_dbputu(av[1],&userrec)) { sosay(0, "database error"); auth_dbclose(); /* Unlock and close DB */ return(0); } syslog(LLEV,"%.100s GROUP USER %.100s TO %.100s",username,av[1],gp_buf); sosay(0, "set group"); auth_dbclose(); /* Unlock and close DB */ return(0); } static int f_addgroup(ac,av,orig,usage) int ac; char *av[]; char *orig; char *usage; { int gp_num, arg_gp_num; char gp_buf[(AUTH_GSIZ+2) * (AUTH_GNUM + 1)] = ""; char cbuf[MAX_STR]; if(!authenticated) { sosay(0, permdenied); return(0); } if((userrecauth.flgs & AUTHFLG_WIZ) == 0) { sosay(0, permdenied); return(0); } if(ac < 3) { showusage(usage); return(0); } if(auth_dbgetu_forupdate(av[1],&userrec)) { /* DB remains locked to avoid a race condition */ sosay(0, "Unknown user"); return(0); } gp_buf[0] = '\0'; for(gp_num=0; gp_num < AUTH_GNUM; gp_num++) { if(userrec.gp[gp_num].name[0] == '\0') break; strlcat(gp_buf, userrec.gp[gp_num].name, sizeof(gp_buf)); strlcat(gp_buf, " ", sizeof(gp_buf)); } for(arg_gp_num = 2; arg_gp_num < ac; arg_gp_num++) { if(strcmplist(av[arg_gp_num], userrec.gp) != -1) { snprintf(cbuf, sizeof(cbuf), "User %s is already a member of the group %s", av[1], av[arg_gp_num]); syslog(LLEV, cbuf); sosay(0, cbuf); return(0); } } for(arg_gp_num = 2; arg_gp_num < ac; arg_gp_num++) { if(gp_num >= AUTH_GNUM) { snprintf(cbuf, sizeof(cbuf), "Max number of groups reached for user %s. Can't assign group %s", av[1], av[arg_gp_num]); syslog(LLEV, cbuf); sosay(0, cbuf); auth_dbclose(); /* Unlock and close DB */ return(0); } strlcpy(userrec.gp[gp_num].name,av[arg_gp_num],AUTH_GSIZ); userrec.gp[gp_num].flgs = 0; strlcat(gp_buf, av[arg_gp_num], sizeof(gp_buf)); strlcat(gp_buf, " ", sizeof(gp_buf)); gp_num++; } if(auth_dbputu(av[1],&userrec)) { sosay(0, "database error"); auth_dbclose(); /* Unlock and close DB */ return(0); } syslog(LLEV,"%.100s GROUP USER %.100s TO %.100s",username,av[1],gp_buf); sosay(0, "set group"); auth_dbclose(); /* Unlock and close DB */ return(0); } static int f_revoke(ac,av,orig,usage) int ac; char *av[]; char *orig; char *usage; { int gp_num, gp_numout, revoked_gnum; char gp_buf[(AUTH_GSIZ+2) * (AUTH_GNUM + 1)] = ""; char cbuf[MAX_STR]; if(!authenticated) { sosay(0, permdenied); return(0); } if((userrecauth.flgs & AUTHFLG_WIZ) == 0) { sosay(0, permdenied); return(0); } if(ac != 3) { showusage(usage); return(0); } if(auth_dbgetu_forupdate(av[1],&userrec)) { /* DB remains locked to avoid a race condition */ sosay(0, "Unknown user"); return(0); } if((revoked_gnum = strcmplist(av[2], userrec.gp)) == -1) { snprintf(cbuf, sizeof(cbuf), "User %s is not a member of the group %s", av[1], av[2]); syslog(LLEV, cbuf); sosay(0, cbuf); return(0); } gp_numout = 0; gp_buf[0] = '\0'; for(gp_num=0; gp_num < AUTH_GNUM; gp_num++) { if(userrec.gp[gp_num].name[0] == '\0') break; if(gp_num == revoked_gnum) continue; strlcat(gp_buf, userrec.gp[gp_num].name , AUTH_GSIZ); strlcat(gp_buf, " ", sizeof(gp_buf)); if (gp_numout != gp_num) memcpy(&userrec.gp[gp_numout], &userrec.gp[gp_num], sizeof(Group)); gp_numout++; } if(gp_num >= AUTH_GNUM) { snprintf(cbuf, sizeof(cbuf), "Max number of groups reached for user %s - possbile database migration error.", av[1]); syslog(LLEV,"fwtksyserr: database group count too high"); sosay(0, cbuf); auth_dbclose(); /* Unlock and close DB */ return(0); } userrec.gp[gp_numout].name[0] = '\0'; if(auth_dbputu(av[1],&userrec)) { sosay(0, "database error"); auth_dbclose(); /* Unlock and close DB */ return(0); } syslog(LLEV,"%.100s GROUP USER %.100s TO %.100s",username,av[1],gp_buf); sosay(0, "set group"); auth_dbclose(); /* Unlock and close DB */ return(0); } static int f_passwd(ac,av,orig,usage) int ac; char *av[]; char *orig; char *usage; { char *usernam; char *password; AProto *aprp; char rbuf[MAX_STR]; if(!authenticated) { sosay(0, permdenied); return(0); } if(ac < 2) { showusage(usage); return(0); } usernam = username; if(ac > 2) { if((userrecauth.flgs & AUTHFLG_WIZ) == 0 && (userrecauth.flgs & AUTHFLG_GWIZ) == 0) { sosay(0, permdenied); return(0); } usernam = av[1]; password = av[2]; } else { password = av[1]; } if(auth_dbgetu_forupdate(usernam,&userrec)) { /* DB remains locked to avoid a race condition */ sosay(0, "Unknown user"); return(0); } if(ac > 2) { if((userrecauth.flgs & AUTHFLG_WIZ) == 0 && ((userrecauth.flgs & AUTHFLG_GWIZ) == 0 || !check_gwiz(userrecauth.gp,userrec.gp))) { sosay(0, permdenied); auth_dbclose(); /* Unlock and close DB */ return(0); } } if((aprp = codetoproto(userrec.atyp)) == (AProto *)0) { syslog(LLEV,"fwtkcfgerr: %.100s: bad auth protocol %c",usernam,userrec.atyp); sosay(0, "Bad authentication protocol for user - abort"); auth_dbclose(); /* Unlock and close DB */ return(0); } if(aprp->setf == 0) { sosay(0, "Password is not settable for this protocol"); auth_dbclose(); /* Unlock and close DB */ return(0); } if(strlen(password) >= AUTH_PWSIZ - 1) { sosay(0, "Password is too long"); auth_dbclose(); /* Unlock and close DB */ return(0); } /* ok, there IS a password setter - call it */ if((*aprp->setf)(usernam,password,&userrec,rbuf)) { sosay(0, rbuf); auth_dbclose(); /* Unlock and close DB */ return(0); } syslog(LLEV,"%.100s CHANGE PASSWORD %.100s",username,usernam); sosay(0, rbuf); time((time_t*)&(userrec.lastpw)); auth_dbclose(); /* Unlock and close DB */ return(0); } static int f_proto(ac,av,orig,usage) int ac; char *av[]; char *orig; char *usage; { AProto *aprp; if(!authenticated) { sosay(0, permdenied); return(0); } if((userrecauth.flgs & AUTHFLG_WIZ) == 0 && (userrecauth.flgs & AUTHFLG_GWIZ) == 0) { sosay(0, permdenied); return(0); } if(ac != 3) { showusage(usage); return(0); } if(auth_dbgetu_forupdate(av[1],&userrec)) { /* DB remains locked to avoid a race condition */ sosay(0, "Unknown user"); return(0); } if((userrecauth.flgs & AUTHFLG_WIZ) == 0 && ((userrecauth.flgs & AUTHFLG_GWIZ) == 0 || !check_gwiz(userrecauth.gp,userrec.gp))) { sosay(0, permdenied); auth_dbclose(); /* Unlock and close DB */ return(0); } if((aprp = nametoproto(av[2])) == (AProto *)0) { char puf[1024]; char ruf[2048]; getprotolist(puf,sizeof(puf)); syslog(LLEV,"fwtkcfgerr: %.100s: bad auth protocol %.100s",av[1],av[2]); snprintf(ruf,sizeof(ruf),"Unknown protocol \"%.100s\", use one of: %s",av[2],puf); sosay(0, ruf); auth_dbclose(); /* Unlock and close DB */ return(0); } if(userrec.atyp != aprp->typ) { userrec.atyp = aprp->typ; userrec.authproto_rec.active = 0; } if(auth_dbputu(av[1],&userrec)) { sosay(0, "database error"); auth_dbclose(); /* Unlock and close DB */ return(0); } syslog(LLEV,"%.100s CHANGE PROTOCOL %.100s",username,av[1]); sosay(0, "changed"); auth_dbclose(); /* Unlock and close DB */ return(0); } /* client expects this command to be multiline. kludgy. */ static int f_display(ac,av,orig,usage) int ac; char *av[]; char *orig; char *usage; { AProto *ap; char buf[1024]; int gp_num; if(!authenticated) { sosay(0, permdenied); if(!interactive) sosay(0, "."); return(0); } if(ac != 2) { showusage(usage); if(!interactive) sosay(0, "."); return(0); } if(auth_dbgetu(av[1],&userrec)) { sosay(0, "Unknown user"); if(!interactive) sosay(0, "."); return(0); } if((userrecauth.flgs & AUTHFLG_WIZ) == 0 && ((userrecauth.flgs & AUTHFLG_GWIZ) == 0 || !check_gwiz(userrecauth.gp,userrec.gp))) { sosay(0, permdenied); if(!interactive) sosay(0, "."); return(0); } /* print user and group */ if(!end_gp_list(userrec.gp,0)) { snprintf(buf,sizeof(buf),"Report for user %s\nGroups",av[1]); for(gp_num=0; gp_num < AUTH_GNUM; gp_num++) { if(end_gp_list(userrec.gp,gp_num)) break; strlcat(buf, " ", sizeof(buf)); strlcat(buf, userrec.gp[gp_num].name, sizeof(buf)); if(userrec.gp[gp_num].flgs & AUTHFLG_GWIZ) strlcat(buf,"(GROUP-WIZARD)",sizeof(buf)); } } else snprintf(buf,sizeof(buf),"Report for user %s",av[1]); if(userrec.ln[0] != '\0') { strlcat(buf," [",sizeof(buf)); strlcat(buf,userrec.ln,sizeof(buf)); strlcat(buf,"]",sizeof(buf)); } sosay(0, buf); /* print last login */ if(userrec.last != 0) { char *ap; ap = ctime((time_t *) &(userrec.last)); ap[24] = '\0'; snprintf(buf,sizeof(buf),"Last authenticated: %s",ap); sosay(0, buf); } /* print last password change */ if(userrec.lastpw != 0) { char *ap; ap = ctime((time_t *) &(userrec.lastpw)); ap[24] = '\0'; snprintf(buf,sizeof(buf),"Last changed password: %s",ap); sosay(0, buf); } /* print bad logins */ if(userrec.bcnt != 0) { snprintf(buf,sizeof(buf),"%d bad logins",userrec.bcnt); sosay(0, buf); } /* misc flags */ if((ap = codetoproto(userrec.atyp)) == (AProto *)0) strlcpy(buf,"Unknown authentication protocol",sizeof(buf)); else snprintf(buf,sizeof(buf),"Authentication protocol: %s",ap->name); sosay(0, buf); strlcpy(buf,"Flags:",sizeof(buf)); if(userrec.flgs & AUTHFLG_WIZ) strlcat(buf," WIZARD",sizeof(buf)); if(userrec.flgs & AUTHFLG_DIS) strlcat(buf," DISABLED",sizeof(buf)); if(buf[6] == '\0') strlcat(buf," none",sizeof(buf)); sosay(0, buf); /* end multiline */ if(!interactive) sosay(0, "."); syslog(LLEV,"%.100s DISPLAY %.100s",username,av[1]); return(0); } static void send_user_listing(user,a) char *user; Auth *a; { AProto *ap; char buf[MAX_STR]; char dp; char gp; char wp = ' '; char *rp; char *an; int gp_num; /* setup status */ if(a->flgs & AUTHFLG_DIS) dp = 'n'; else if(a->flgs & AUTHFLG_BSLP) dp = 'b'; else dp = 'y'; if(a->flgs & AUTHFLG_WIZ) wp = 'W'; gp = (!end_gp_list(a->gp,0) && (a->gp[0].flgs & AUTHFLG_GWIZ))? 'G':' '; /* last login */ if(a->last == 0) rp = "never"; else { rp = ctime((time_t*) &(a->last)); rp[24] = '\0'; } /* auth protocol */ if((ap = codetoproto(a->atyp)) == (AProto *)0) an = "unknown"; else an = ap->name; snprintf(buf,sizeof(buf),"%-12.12s %-12.12s %-12.12s%c%c%c %-12.12s %-24.24s",user,a->gp[0].name,a->ln,dp,wp,gp,an,rp); sosay(0, buf); if (end_gp_list(a->gp,0) || end_gp_list(a->gp,1)) return; for (gp_num = 1; (gp_num < AUTH_GNUM) && !end_gp_list(a->gp,gp_num); gp_num++) { snprintf(buf,sizeof(buf)," %-13.13s %c", a->gp[gp_num].name, (a->gp[gp_num].flgs & AUTHFLG_GWIZ) ? 'G':' '); sosay(0, buf); } } /* client expects this command to be multiline. kludgy. */ static int f_list(ac,av,orig,usage) int ac; char *av[]; char *orig; char *usage; { char buf[MAX_STR]; Auth abuf; int rc; int wiz; if(!authenticated) { sosay(0, permdenied); if(!interactive) sosay(0, "."); return(0); } wiz = userrecauth.flgs & AUTHFLG_WIZ; if(ac > 2) { showusage(usage); if(!interactive) sosay(0, "."); return(0); } if(ac > 1) { Group gp[2]; gp[0].flgs = 0; strlcpy(gp[0].name, av[1], AUTH_GSIZ); gp[1].name[0] = '\0'; gp[1].name[1] = '\0'; if(wiz == 0 && ((userrecauth.flgs & AUTHFLG_GWIZ) == 0 || !check_gwiz(userrecauth.gp, gp))) { sosay(0, permdenied); if(!interactive) sosay(0, "."); return(0); } } if(ac > 1) { syslog(LLEV,"%.100s LIST %.100s",username,av[1]); snprintf(buf,sizeof(buf),"Report for users in group %.100s",av[1]); } else { syslog(LLEV,"%.100s LIST",username); snprintf(buf,sizeof(buf),"Report for users in database"); } sosay(0, buf); sosay(0, "user group longname status proto last"); sosay(0, "---- -------- -------- --- ------------ ----"); if(auth_dbopen()) { sosay(0, "Cannot open database."); if(!interactive) sosay(0, "."); return(0); } rc = auth_dbtraversestart(buf,&abuf); while (rc == 0) { if(ac < 2 || ((ac > 1) && (strcmplist(av[1], abuf.gp)) != -1)) send_user_listing(buf,&abuf); rc = auth_dbtraversenext(buf,&abuf); } auth_dbclose(); /* end multiline */ if(!interactive) sosay(0, "."); return(0); } /* generate a bogus challenge for users w/o accounts */ static int randomchallenge(c,siz) char *c; int siz; { extern long randomnumber(); int len; for(len = 0; len < siz; len++) c[len] = (char)(((u_int)randomnumber() % 10) + '0'); c[siz] = '\0'; return(0); } /* if we somehow have a bogus user give them a bogus challenge */ static int bogusprompt() { char cbuf[128]; strlcpy(cbuf,"challenge Challenge \"",sizeof(cbuf)); randomchallenge(&cbuf[21],6); strlcat(cbuf,"\": ",sizeof(cbuf)); sosay(0, cbuf); return(0); } Cfg* authsrv_init(int interactive) { char *s; memset(proxy_name,0,sizeof(proxy_name)); memset(proxy_chroot,0,sizeof(proxy_chroot)); strlcpy(proxy_name,"authsrv",sizeof(proxy_name)); #ifndef LOG_DAEMON openlog(proxy_name,LOG_PID); #else openlog(proxy_name,LOG_PID|LOG_NDELAY,LFAC); #endif /* We lack fastdaemon fucntions here yet. To be added later. Maybe. It is PITA to unify. */ if ((proxy_confp = cfg_read(proxy_name)) == (Cfg *)-1) { syslog(LLEV,"fwtkcfgerr: no netperm-table records for %.128s",proxy_name); exit(1); } proxy_conf_groupid(proxy_confp); proxy_conf_userid(proxy_confp); proxy_timeout = proxy_conf_int(proxy_confp,"timeout",1,INT_MAX,PROXY_TIMEOUT); proxy_stats.child_limit = proxy_conf_int(proxy_confp,"maxchildren",1,4096,0); sotimeout(proxy_timeout); if ((s = proxy_conf_string(proxy_confp,"directory"))) strlcpy(proxy_chroot,s,sizeof(proxy_chroot)); time(&proxy_stats.start_time); if (!interactive && get_sock_type(0) == AF_INET && peername(0,proxy_stats.rladdr,proxy_stats.riaddr,sizeof(proxy_stats.rladdr))) { if (errno==ENOTCONN) syslog (LLEV, "lost connection"); else syslog (LLEV, "fwtksyserr: cannot get peer name: %s", strerror(errno)); exit(1); } proxy_stats.inbytes = 0; proxy_stats.outbytes = 0; proxy_update_status(); return(proxy_confp); } void log_usrmsg(char *msg, char *prm1, char *prm2_pat, char *prm2) { char buf[MAX_STR]; strlcpy(buf, msg, sizeof(buf)); if(prm2[0] != '\0') { strlcat(buf, prm2_pat, sizeof(buf)); syslog(LLEV, buf, prm1, prm2); } else syslog(LLEV, buf, prm1); } void showusage(char *usage) { char buf[MAX_STR] = "Usage: "; strlcat(buf, usage, sizeof(buf)); sosay(0, buf); } int check_auth_console(char *cons_sockname, char auth_req[]) { struct sockaddr_un sn; char buf[MAX_STR]; int cons_fd; struct stat stat_buf; char *pwd_tok, *p; static uint32_t auth_req_num = 0; if(stat(cons_sockname, &stat_buf) == 0 && S_ISSOCK(stat_buf.st_mode)) { cons_fd = socket(PF_UNIX, SOCK_STREAM, 0); if (cons_fd < 0) { syslog (LLEV, "fwtksyserr: socket(): %s", strerror(errno)); return NO_CONS; } strlcpy(sn.sun_path, cons_sockname, UNIX_PATH_MAX); sn.sun_family = AF_UNIX; if (connect(cons_fd, (struct sockaddr *)&sn, sizeof(sn)) < 0) { syslog (LLEV, "fwtksyserr: connect(): %s", strerror(errno)); return NO_CONS; } if(cons_fd < 0) { return NO_CONS; } if(sosay(cons_fd, auth_req)) { close(cons_fd); return NO_CONS; } if (sogets(cons_fd, buf, sizeof(buf)) <= 0) { close(cons_fd); return NO_CONS; } if(!strncasecmp(buf, "auth", strlen("auth"))) { if((pwd_tok = strchr(buf, ' ')) != NULL && strlen(++pwd_tok) > 0) /* password auth with console-generated pwd */ { if((p=strrchr(buf, '\n')) != NULL) *p='\0'; strlcpy(console_pwd, pwd_tok, sizeof(console_pwd)); } auth_req_num++; return CONS_AUTH; } else { sowrite(0, "console: ",9); sosay(0, buf); return CONS_DENY; } } return(NO_CONS); } int makeup_cons_sockname(char *sockdir, char *host, char *username, char *sockname) { if(host != NULL && strlen(host) > 0 && strcmp(host, "unknown")) { strlcpy(sockname, sockdir, UNIX_PATH_MAX); strlcat(sockname, "/", UNIX_PATH_MAX); strlcat(sockname, host, UNIX_PATH_MAX); strlcat(sockname, ".", UNIX_PATH_MAX); strlcat(sockname, username, UNIX_PATH_MAX); strlcat(sockname, ".sock", UNIX_PATH_MAX); return 1; } return 0; } sa_family_t get_sock_type(int fd) { socklen_t addrlen; struct sockaddr_un sua; struct sockaddr *sa = (struct sockaddr *) &sua; struct sockaddr_in *sia = (struct sockaddr_in *)&sua; int protoval; socklen_t protolen; char tbuf[1500]; addrlen = sizeof(struct sockaddr_un); protolen = sizeof(int); if (getsockopt(fd,SOL_SOCKET,SO_TYPE,(char *)&protoval,&protolen)) { syslog(LLEV,"fwtksyserr: cannot get protocol id, %s", strerror(errno)); return(1); } switch (protoval) { case SOCK_STREAM: if (getpeername(fd,(struct sockaddr *)sa,&addrlen)<0) { if (errno!=ENOTCONN) syslog(LLEV,"fwtksyserr: getpeername failed: %s", strerror(errno)); return(1); } break; case SOCK_DGRAM: if (recvfrom(fd,tbuf,sizeof(tbuf),MSG_PEEK, (struct sockaddr *)sa,&addrlen) < 0) { syslog(LLEV,"fwtksyserr: UDP socket error %s", strerror(errno)); return(1); } if (connect(fd,(struct sockaddr *)sa,sizeof(*sa))) { syslog(LLEV,"fwtksyserr: UDP socket connect error %s", strerror(errno)); return(1); } break; default: syslog(LLEV,"fwtksyserr: unknown protocol %d",protoval); return(1); } if(sia->sin_family == AF_INET) return AF_INET; else if(sua.sun_family == AF_UNIX) return AF_UNIX; else return (sa_family_t) -1; } Cfg* authsrv_proxy_conf_hosts (Cfg *confp, char *rladdr, char *riaddr) { Cfg *cf = cfg_get ("hosts", confp); int i; while (cf != (Cfg *)0) { for (i = 0; i < cf->argc && cf->argv[i][0] != '-'; ++i) { if (hostmatch (cf->argv[i], riaddr)) { if (cf->flags & PERM_DENY) goto deny; return cf; } } cf = cfg_get ("hosts", (Cfg *)0); } deny: syslog (LLEV, "deny host=%s/%s use of gateway", rladdr, riaddr); return (Cfg *)0; } Cfg * proxy_get_match(char *tag, const Group gp[], char* arg[]) { Cfg* cfp; int top; /* process netperm strings */ for(cfp = cfg_get(tag, proxy_confp); cfp != (Cfg *) 0; cfp = cfg_get(tag, 0)) { if (cfp->argc < 3) { syslog(LLEV,"fwtkcfgerr: improperly formatted '%s' record, line %d",tag, cfp->ln); continue; } if(strcmp(cfp->argv[0], "group") == 0) { if(!nacasematchlist(cfp->argv[1], gp)) continue; } else if(strcmp(cfp->argv[0], "user") == 0) { if(!nacasematch(cfp->argv[1], arg[0])) continue; } else { syslog(LLEV,"fwtkcfgerr: '%s' should specify user or group, line %d", tag, cfp->ln); sosay(0, "operation: syntax error in netperm-table"); return (NULL); } if(!nacasematch(cfp->argv[2],arg[1])) continue; if(!hostmatch(cfp->argv[3], arg[2]) && !nacasematch(cfp->argv[3], arg[2])) continue; top = cfp->argc; if(cfp->argc > 7 && !strcmp(cfp->argv[cfp->argc - 3],"time")) top = cfp->argc - 3; if(strcmp(tag, "operation") == 0) { int match = 1; int counter; char** interven_arg = arg + 3; /* checl destination */ if(!hostmatch(cfp->argv[4], arg[3]) && !nacasematch(cfp->argv[4], arg[3])) continue; /* match intervening tokens (if any) up to possible "time" field */ for(counter = 5; counter < top; counter++) { if(!(match = nacasematch(cfp->argv[counter], *interven_arg ? *interven_arg++ : "blech"))) break; } if(!match) continue; } else if(top > 4) { /* authentication */ syslog(LLEV,"fwtkcfgerr: '%s' entry syntax is incorrect, \"time\" expected, line %d", tag, cfp->ln); sosay(0, "operation: syntax error in netperm-table"); return (NULL); } if(cfp->argc != top) { if(time_ok(cfp->argv[cfp->argc - 1], cfp->argv[cfp->argc - 2]) != 0) continue; } break; } return cfp; }