/* * Copyright (c) 1998, 1999, 2000, 2001 * Gesellschaft fuer wissenschaftliche * Datenverarbeitung mbH Goettingen. All rights reserved. * * Redistribution and use in source and binary forms, with or without * modification, are permitted provided that the following conditions * are met: * 1. Redistributions of source code must retain the above copyright * notice, this list of conditions and the following disclaimer. * 2. Redistributions in binary form must reproduce the above copyright * notice, this list of conditions and the following disclaimer in the * documentation and/or other materials provided with the distribution. * 3. All advertising materials mentioning features or use of this software * must display the following acknowledgement: * This product includes software developed by the Gesellschaft * fuer wissenschaftliche Datenverarbeitung mbH Goettingen. * 4. The name of the Gesellschaft fuer wissenschaftliche Datenverarbeitung * mbH Goettingen may not be used to endorse or promote products derived * from this software without specific prior written permission. * * THIS SOFTWARE IS PROVIDED BY THE GESELLSCHAFT FUER WISSENSCHAFTLICHE * DATENVERARBEITUNG MBH GOETTINGEN ``AS IS'' AND ANY EXPRESS OR IMPLIED * WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES OF * MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED. * IN NO EVENT SHALL THE GESELLSCHAFT FUER WISSENSCHAFTLICHE DATENVERARBEITUNG * MBH GOETTINGEN BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, * EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, * PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; * OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, * WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR * OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF * ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. */ #ifndef lint static const char copyright[] = "@(#) Copyright (c) 1998, 1999, 2000\n\ Gesellschaft fuer wissenschaftliche\n\ Datenverarbeitung mbH Goettingen. All rights reserved.\n"; static const char rcsid[] = "$Id: useracc.c,v 3.1 2002/12/20 09:14:16 kheuer Exp $"; #endif /* not lint */ /* * Account management program; client-server implementation. * Improvements projected: security, search algorithm. * * The code has been developed for use on hardware driven by the FreeBSD * operating system. Portability to other operating systems has not been * an important design goal. Nevertheless, ports to operating systems * providing the typical BSD API may be possible without too much effort. * * Compile, link and install commands: * * cc -DFreeBSD -O useracc.c -lcrypt -o useracc -s * * 1998/12/29 initial usable version * 1999/01/04 minor improvement * 1999/01/05 minor improvement * 1999/01/18 client (now: slave) uses secure port for privileged access * 1999/03/25 port to FreeBSD 3.1-RELEASE * 1999/10/11 improved error messages * 1999/10/21 ignore SIGHUP * 2000/01/26 20-1 replaced by MAXSTRLEN-1 in strncpy in * function server; subsequently completely removed * 2000/04/27 finished with more extensive revision introducing * master-slave-server concept * 2000/05/08 minor bug fix * 2001/03/13 access rights refined, edit mode added * 2001/03/19 minor bug fix * 2002/12/20 RCS version 3.1 check in */ #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #define ACCFILE "useracc.dat" /* default user accounts file */ #define EDITOR "vi" /* default text editor */ #define KACOM "ASSIGN KEY" /* log file comment in case of key request*/ #define LOCALHOST "localhost" /* default server host */ #define LOGFILE "/dev/stdout" /* default log file */ #define MAXBFILES 999 /* maximal number of backup files */ #define MAXKEYLEN 32 /* maximal encrypted(!) key string length */ #define MAXRPORT 1023 /* highest reserved port number */ #define MAXSTRLEN 256 /* maximal string length & record length */ #define MAXUSRLEN 16 /* maximal user name length */ #define NPENDCON 8 /* maximal number of pending connections */ #define MPORT 911 /* default master server tcp port */ #define SPORT 912 /* default slave server tcp port */ #define TIMEOUT 12 /* default connection timeout */ #define WILDCARD "*" /* wildcard character substituting host name */ /* to print error messages */ #define errmsg(s) \ ( \ args.daemon ? syslog(LOG_ERR, "%s: %s\n", s, strerror(errno)) : \ fprintf(stderr, "%s: %s: %s\n", pgm, s, strerror(errno)) \ ) #define herrmsg(s) \ ( \ args.daemon ? syslog(LOG_ERR, "%s: %s\n", s, hstrerror(h_errno)) : \ fprintf(stderr, "%s: %s: %s\n", pgm, s, hstrerror(h_errno)) \ ) enum { /* codes to request server operations */ assign_key, read_all, read_balance, read_info, write_balance, write_info }; enum { /* error codes */ no_error, data_incompl, inval_data, inval_key, key_ass_fail, master_unacc, perm_denied, slave_unacc, user_n_found }; enum { /* program call type */ master = 1, slave }; typedef struct { /* evaluated command line arguments */ int accdesc; char accfile[MAXSTRLEN]; int balance; char comment[MAXSTRLEN]; int daemon; int edit; int grant; union { char hostname[MAXSTRLEN]; char hostlist[MAXSTRLEN]; } hostspec; int info; char logfile[MAXSTRLEN]; FILE *logptr; int mport; int read; int server; int sport; int timeout; union { char user[MAXSTRLEN]; char userlist[MAXSTRLEN]; } userspec; char value[MAXSTRLEN]; int write; int xtract; } args_t; typedef struct { /* account file data record */ char user[MAXUSRLEN]; double balance; char userinfo[MAXSTRLEN]; char hostname[MAXSTRLEN - MAXUSRLEN - sizeof(double)]; } rec_t; typedef struct { /* client/server transmit data */ char comment[MAXSTRLEN]; char ruser[MAXUSRLEN]; char user[MAXUSRLEN]; char value[MAXSTRLEN]; u_short errnum; u_short grant; u_short opcode; u_short salt; char key[MAXKEYLEN]; #ifndef OSF1 u_long secs; u_long seed; #else u_int secs; u_int seed; #endif } xm_t; int client ( void ); /* main client code */ /* connect to server */ int con2serv ( char *, int, int * ); int edit ( void ); /* edit account file */ int isinlist ( char *, char *);/* look for string in comma-delimited list */ /* lookup user in account file */ int lookup ( char *, rec_t *); void onexit ( void ); /* exit function */ int recvdata ( int, xm_t * ); /* receive data */ char *rndkey ( void ); /* generate random key */ int senddata ( int, xm_t * ); /* send data */ int server ( void ); /* main server code */ void sighdl ( int ); /* signal handler */ char *timestr ( void ); /* returns local time for logging */ /* update user entry in account file */ int update ( rec_t *, xm_t *, char *); void usage ( void ); /* prints usage note */ /* setup record to transmit */ void xmsetup ( xm_t *, char * ); int xtract ( void ); /* extract data from account file */ args_t args; /* evaluated command line arguments */ char *pgm; /* program name */ jmp_buf env; /* long jump buffer to handle SIGALRM */ struct passwd *pw; /* user information */ time_t seed; /* seed for random number generation */ static char *errmsg[] = { /* server error messages */ "Data transfer incomplete", "Invalid data", "Invalid key", "Key assignment failed", "Master server unaccessible", "Permission denied", "Slave server unaccessible", "User not found in account file" }; int client ( ) /* ----- client ----- */ { int sock; xm_t xmit; retry: /* retry request */ if (con2serv(LOCALHOST, args.sport, &sock) < 0) { fprintf(stderr, "%s: %s\n", pgm, errmsg[slave_unacc - 1]); return sock; } xmsetup(&xmit, NULL); if (senddata(sock, &xmit) < 0) { errmsg("write"); fprintf(stderr, "%s: %s\n", pgm, errmsg[0]); return EX_SOFTWARE; } if (recvdata(sock, &xmit) < 0) { errmsg("read"); fprintf(stderr, "%s: %s\n", pgm, errmsg[0]); return EX_SOFTWARE; } close(sock); if ((xmit.errnum = ntohs(xmit.errnum)) == inval_key) goto retry; else if (xmit.errnum) { fprintf(stderr, "%s: %s\n", pgm, errmsg[--xmit.errnum]); return EX_SOFTWARE; } else if (args.read) printf("%s\n", xmit.value); return 0; } /* ----- con2serv ----- */ int con2serv ( char *hostname, int port, int *sock ) { int sockfd; #ifndef OSF1 unsigned long inadd; #else in_addr_t inadd; #endif struct hostent *host; struct sockaddr_in client_addr; struct sockaddr_in server_addr; memset(&client_addr, 0, sizeof client_addr); memset(&server_addr, 0, sizeof server_addr); if ((inadd = inet_addr(hostname)) == INADDR_NONE) { if (!(host = gethostbyname(hostname))) { herrmsg("gethostbyname"); *sock = EX_SOFTWARE; return -1; } server_addr.sin_family = host->h_addrtype; memcpy(&server_addr.sin_addr, host->h_addr, host->h_length); } else { server_addr.sin_family = PF_INET; server_addr.sin_addr.s_addr = inadd; } server_addr.sin_port = htons(port); if ((sockfd = socket(PF_INET, SOCK_STREAM, 0)) < 0) { errmsg("socket"); *sock = EX_SOFTWARE; return -1; } client_addr.sin_port = 0; client_addr.sin_family = AF_INET; client_addr.sin_addr.s_addr = INADDR_ANY; if (!getuid()) { if (bindresvport(sockfd, &client_addr) == -1) { errmsg("bindresvport"); *sock = EX_SOFTWARE; return -1; } } if (signal(SIGALRM, sighdl) == SIG_ERR) { errmsg("signal"); *sock = EX_SOFTWARE; return -1; } if (!setjmp(env)) { alarm(args.timeout); while (connect(sockfd, (struct sockaddr *) &server_addr, sizeof server_addr) < 0); alarm(0); *sock = sockfd; return 0; } else { errmsg("connect"); *sock = EX_SOFTWARE; return -1; } } int edit ( ) /* ----- edit ----- */ { char bckf[MAXSTRLEN]; char buf[sizeof(rec_t)]; char chr; char *editor = getenv("EDITOR") ? getenv("EDITOR") : EDITOR; char tmpf[MAXSTRLEN]; int bdesc; int field; int fmterr; int i; int ic; int len; int nbytes; int ncolons; int nl; int nrecsr; int nrecsw = 0; int rc = 0; int reedit; int status; int tdesc = -1; pid_t pid; rec_t rec; if (!isatty(0) || !isatty(1)) { fprintf(stderr, "%s: tty required for stdin and stdout in edit mode\n", pgm); return EX_SOFTWARE; } for (i = 0; i <= MAXBFILES; i++) { #ifndef FreeBSD sprintf(bckf, "%s.%3.3d", args.accfile, i); #else snprintf(bckf, MAXSTRLEN, "%s.%3.3d", args.accfile, i); #endif if ((bdesc = open(bckf, O_RDWR | 0)) == -1) break; else close(bdesc); } bdesc = -1; #ifndef FreeBSD sprintf(tmpf, "/tmp/%s.XXXXXX", pgm); #else snprintf(tmpf, MAXSTRLEN, "/tmp/%s.XXXXXX", pgm); #endif if ((tdesc = mkstemp(tmpf)) == -1) { errmsg("mkstemp"); rc = EX_IOERR; goto cleanup; } #ifndef FreeBSD if ((args.accdesc = open(args.accfile, O_RDWR | O_CREAT | O_NONBLOCK, 0)) == -1 || (bdesc = open(bckf, O_RDWR | O_CREAT | O_NONBLOCK, 0)) == -1 ) { errmsg("open"); rc = EX_IOERR; goto cleanup; } #else if ((args.accdesc = open(args.accfile, O_RDWR | O_CREAT | O_EXLOCK | O_NONBLOCK, 0)) == -1 || (bdesc = open(bckf, O_RDWR | O_CREAT | O_NONBLOCK, 0)) == -1 ) { errmsg("open"); rc = EX_IOERR; goto cleanup; } #endif if (fchmod(args.accdesc, S_IRUSR | S_IWUSR) == -1 || fchmod(bdesc, S_IRUSR | S_IWUSR) == -1 ) { errmsg("fchmod"); rc = EX_IOERR; goto cleanup; } printf("\nBacking up account file data ... "); while ((nbytes = read(args.accdesc, &rec, sizeof(rec_t))) == sizeof(rec_t)) if (write(bdesc, &rec, sizeof(rec_t)) != sizeof(rec_t)) { errmsg("write"); rc = EX_IOERR; goto cleanup; } if (nbytes == -1) { errmsg("read"); rc = EX_IOERR; goto cleanup; } if (fsync(bdesc) == -1) { errmsg("fsync"); rc = EX_IOERR; goto cleanup; } close(bdesc), bdesc = -1; printf("done.\n"); if (lseek(args.accdesc, 0, SEEK_SET) == -1) { errmsg("lseek"); rc = EX_SOFTWARE; goto cleanup; } printf("\nExtracting data from account file ... "); while ((nbytes = read(args.accdesc, &rec, sizeof(rec_t))) == sizeof(rec_t)) { #ifndef FreeBSD sprintf(buf, "%s:%0.4f:%s:%s\n", rec.user, rec.balance, rec.userinfo, rec.hostname); #else snprintf(buf, sizeof (rec_t), "%s:%0.4f:%s:%s\n", rec.user, rec.balance, rec.userinfo, rec.hostname); #endif len = strlen(buf); if (write(tdesc, buf, len) != len) { errmsg("write"); rc = EX_IOERR; goto cleanup; } nrecsw++; } if (nbytes == -1) { errmsg("read"); rc = EX_IOERR; goto cleanup; } printf("done.\n"); do { if ((pid = fork()) == -1) { errmsg("fork"); rc = EX_OSERR; goto cleanup; } else if (pid == 0) { if (execlp(editor, editor, tmpf, NULL) == - 1) { errmsg("execlp"); rc = EX_SOFTWARE; goto cleanup; } } else { if (wait(&status) == -1) { errmsg("wait"); rc = EX_SOFTWARE; goto cleanup; } } if (!WIFEXITED(status) || WEXITSTATUS(status)) { fprintf(stderr, "%s: editor %s exited abnormally - abort!\n", pgm, editor); rc = EX_SOFTWARE; goto cleanup; } if (lseek(tdesc, 0, SEEK_SET) == -1) { errmsg("lseek"); rc = EX_SOFTWARE; goto cleanup; } fmterr = 0; ncolons = 0; nrecsr = 0; while ((nbytes = read(tdesc, buf, sizeof(rec_t)))) { for (i = 0; i < nbytes; i++) { if (buf[i] == ':') ncolons++; if (buf[i] == '\n') { nrecsr++; if (ncolons != 3) { fmterr = 1; printf("\nFile format ERROR in line: %d", nrecsr); } ncolons = 0; } } } if (nbytes == -1) { errmsg("read"); rc = EX_IOERR; goto cleanup; } printf("\n"); printf("Number of records before editing: %d\n", nrecsw); printf("Number of records after editing: %d\n", nrecsr); printf("Is this ok?\n\n"); if (fmterr) printf("Because of ERRORS, you must re-edit, " "or your changes will be lost!\n\n"); printf("Re-edit? (y/n) [y] "); while ((reedit = getchar()) == '\n'); if (reedit != 'n') reedit = 'y'; printf("\n"); } while (reedit == 'y'); if (fmterr) { rc = EX_SOFTWARE; goto cleanup; } if (lseek(tdesc, 0, SEEK_SET) == -1) { errmsg("lseek"); rc = EX_SOFTWARE; goto cleanup; } if (lseek(args.accdesc, 0, SEEK_SET) == -1) { errmsg("lseek"); rc = EX_SOFTWARE; goto cleanup; } printf("Rebuilding account file ... "); field = 0; i = 0; memset(&rec, sizeof rec, 0); while ((nbytes = read(tdesc, &chr, 1))) { ic = (chr == ':') ? 1 : 0; nl = (chr == '\n') ? 1 : 0; if (!ic && !nl) buf[i++] = chr; else { buf[i++] = '\0'; i = 0; switch (field) { case 0: strncpy(rec.user, buf, MAXUSRLEN - 1); break; case 1: rec.balance = atof(buf); break; case 2: strncpy(rec.userinfo, buf, MAXSTRLEN - 1); break; case 3: strncpy(rec.hostname, buf, MAXSTRLEN - MAXUSRLEN - sizeof(double) - 1); break; default: fprintf(stderr, "%s: serious internal error while generating account file\n", pgm); break; } if (ic) field++; if (nl) { field = 0; if ((write(args.accdesc, &rec, sizeof rec)) == -1) { errmsg("write"); rc = EX_IOERR; goto cleanup; } memset(&rec, sizeof rec, 0); } } } if (nbytes == -1) { errmsg("read"); rc = EX_IOERR; goto cleanup; } if (ftruncate(args.accdesc, (off_t) nrecsr * sizeof(rec_t)) == -1) { errmsg("ftruncate"); rc = EX_IOERR; goto cleanup; } printf("done.\n\n"); printf("Backup file is: %s\n", bckf); cleanup: if (args.accdesc != -1) { if (fsync(args.accdesc) == -1) { errmsg("fsync"); if (!rc) rc = EX_IOERR; } close(args.accdesc); } if (bdesc != -1) close(bdesc); if (tdesc != -1) close(tdesc); (void) unlink(tmpf); return rc; } /* ----- isinlist ----- */ int isinlist ( char *item, char *list ) { char buf[MAXSTRLEN]; char *tok; int rc = 0; #ifndef FreeBSD sprintf(buf, "%s,", list); #else snprintf(buf, MAXSTRLEN, "%s,", list); #endif tok = strtok(buf, ","); do { if (!strcmp(item, tok)) rc = 1; } while (tok = strtok(NULL, ",")); return rc; } /* ----- lookup ----- */ int lookup ( char *user, rec_t *rec ) { off_t lpos = 0; if (lseek(args.accdesc, 0, SEEK_SET) == -1) { errmsg("lseek"); exit(EX_SOFTWARE); } while (read(args.accdesc, rec, sizeof(rec_t)) == sizeof(rec_t)) { if (!strcmp(user, rec->user)) { if (lseek(args.accdesc, lpos, SEEK_SET) == -1) { errmsg("lseek"); exit(EX_SOFTWARE); } else return 0; } lpos += sizeof(rec_t); } return -1; } void onexit ( ) /* ----- onexit ----- */ { if (args.logptr) { fprintf(args.logptr, "%s T %s\n", timestr(), pgm); fclose(args.logptr); } if (args.accdesc != -1) close(args.accdesc); } /* ----- recvdata ----- */ int recvdata ( int fd, xm_t *xmit ) { int nread = 0; memset(xmit, 0, sizeof(xm_t)); if (!setjmp(env)) { alarm(args.timeout); do { nread += read(fd, xmit + nread, sizeof(xm_t) - nread); } while (nread < sizeof(xm_t)); alarm(0); return 0; } else return -1; } /* ----- rndkey ----- */ char *rndkey ( ) { static char buf[MAXKEYLEN]; int i; memset(buf, 0, sizeof buf); for (i = 0; i < MAXKEYLEN / 4; i++) buf[i] = 'a' + (char) (random() % 26); return buf; } /* ----- senddata ----- */ int senddata ( int fd, xm_t *xmit ) { int nwrite = 0; if (!setjmp(env)) { alarm(args.timeout); do { nwrite += write(fd, xmit + nwrite, sizeof(xm_t) - nwrite); } while (nwrite < sizeof(xm_t)); alarm(0); return 0; } else return -1; } int server ( ) /* ----- server ----- */ { char buf[MAXKEYLEN]; char *key = rndkey(); char host[MAXSTRLEN]; clock_t reqbeg; clock_t reqend; double esecs; int addrlen; int hostok; int keyok; int lu; int newsock; int port; int portok; int sock; int sock2mas; int timeok; int userok; pid_t pid; rec_t rec; struct hostent *client_host; struct sockaddr_in client_addr; struct sockaddr_in server_addr; xm_t xmit; if (args.daemon) { if ((pid = fork()) == -1) { errmsg("fork"); return EX_OSERR; } else if (pid) return 0; } if (atexit(onexit)) { errmsg("atexit"); return EX_SOFTWARE; } if (signal(SIGALRM, sighdl) == SIG_ERR || signal(SIGHUP, sighdl) == SIG_ERR || signal(SIGINT, sighdl) == SIG_ERR || signal(SIGQUIT, sighdl) == SIG_ERR || signal(SIGTERM, sighdl) == SIG_ERR) { errmsg("signal"); return EX_SOFTWARE; } if (args.server == master) { #ifndef FreeBSD if ((args.accdesc = open(args.accfile, O_RDWR | O_CREAT | O_NONBLOCK, 0)) == -1) { errmsg("open"); return EX_IOERR; } #else if ((args.accdesc = open(args.accfile, O_RDWR | O_CREAT | O_EXLOCK | O_NONBLOCK, 0)) == -1) { errmsg("open"); return EX_IOERR; } #endif if (fchmod(args.accdesc, S_IRUSR | S_IWUSR) == -1) { errmsg("fchmod"); return EX_IOERR; } if (!(args.logptr = fopen(args.logfile, "a"))) { errmsg("fopen"); return EX_IOERR; } if (strcmp(args.logfile, "/dev/stdout") && chmod(args.logfile, S_IRUSR | S_IWUSR) == -1) { errmsg("chmod"); return EX_IOERR; } fprintf(args.logptr, "%s S %s d=%1d f=%s h=%s l=%s p=%1d s=%1d t=%1d u=%s\n", timestr(), pgm, args.daemon, args.accfile, args.hostspec.hostlist, args.logfile, args.mport, args.server, args.timeout, args.userspec.userlist); fflush(args.logptr); } if ((sock = socket(PF_INET, SOCK_STREAM, 0)) < 0) { errmsg("socket"); return EX_SOFTWARE; } memset(&server_addr, 0, sizeof server_addr); server_addr.sin_family = PF_INET; if (args.server == master) { server_addr.sin_port = htons(args.mport); server_addr.sin_addr.s_addr = htonl(INADDR_ANY); } else { server_addr.sin_port = htons(args.sport); server_addr.sin_addr.s_addr = htonl(INADDR_LOOPBACK); } if (bind(sock, (struct sockaddr *) &server_addr, sizeof server_addr) < 0) { errmsg("bind"); return EX_SOFTWARE; } if (listen(sock, NPENDCON) < 0) { errmsg("listen"); return EX_SOFTWARE; } for (;;) { addrlen = sizeof client_addr; if ((newsock = accept(sock, (struct sockaddr *) &client_addr, &addrlen)) < 0) { errmsg("accept"); continue; } if (args.server == master) reqbeg = clock(); port = ntohs(client_addr.sin_port); if (!(client_host = gethostbyaddr((char *) &client_addr.sin_addr, sizeof(struct in_addr), AF_INET))) { herrmsg("gethostbyaddr"); continue; } strncpy(host, client_host->h_name, MAXSTRLEN - 1); if (recvdata(newsock, &xmit) < 0) { if (args.server == master) { fprintf(args.logptr, "%s E %-30s %5d receive error\n", timestr(), host, port); fflush(args.logptr); } close(newsock); continue; } if (args.server == slave) { if (strcmp(host, LOCALHOST)) xmit.errnum = perm_denied; else { if (con2serv(args.hostspec.hostname, args.mport, &sock2mas) < 0) xmit.errnum = master_unacc; else { xmsetup(&xmit, key); if (senddata(sock2mas, &xmit) < 0) xmit.errnum = data_incompl; else { if (recvdata(sock2mas, &xmit) < 0) xmit.errnum = data_incompl; else xmit.errnum = ntohs(xmit.errnum); } } close(sock2mas); } memset(xmit.key, 0, sizeof xmit.key); xmit.errnum = htons(xmit.errnum); xmit.salt = htons(0); xmit.secs = htonl(0); (void) senddata(newsock, &xmit); close(newsock); if (ntohs(xmit.errnum) == inval_key) { memset(&xmit, 0, sizeof xmit); strncpy(xmit.comment, KACOM, MAXSTRLEN - 1); xmit.opcode = htons(assign_key); if (con2serv(args.hostspec.hostname, args.mport, &sock2mas) < 0) xmit.errnum = master_unacc; else { if (senddata(sock2mas, &xmit) < 0) xmit.errnum = data_incompl; else { if (recvdata(sock2mas, &xmit) < 0) xmit.errnum = data_incompl; else xmit.errnum = ntohs(xmit.errnum); } close(sock2mas); } if (xmit.errnum) { args.daemon ? syslog(LOG_ERR, "%s\n", errmsg[--xmit.errnum]) : fprintf(stderr, "%s: %s\n", pgm, errmsg[--xmit.errnum]); } else { srandom(seed = ntohl(xmit.seed)); key = rndkey(); } } continue; } xmit.opcode = ntohs(xmit.opcode); xmit.salt = ntohs(xmit.salt); xmit.secs = ntohl(xmit.secs); hostok = isinlist(host, args.hostspec.hostlist); keyok = !strcmp(crypt(key, (char *) &xmit.salt), xmit.key); portok = args.mport <= MAXRPORT ? (port <= MAXRPORT) : 1; timeok = abs((long) time(NULL) - (long) xmit.secs) <= args.timeout; userok = !strcmp(xmit.ruser, xmit.user) || isinlist(xmit.ruser, args.userspec.userlist); if (hostok && portok && xmit.opcode == assign_key) { if (args.daemon) syslog(LOG_ALERT, "key assignment on host: %s\n", host); else fprintf(stderr, "%s: key assignment on host: %s\n", pgm, host); xmit.errnum = no_error; xmit.seed = htonl(seed); } else if (!keyok) xmit.errnum = inval_key; else if (!hostok || !portok || !timeok || !userok) xmit.errnum = perm_denied; else { xmit.errnum = no_error; if ((lu = lookup(xmit.user, &rec)) == 0) if (args.grant || !strcmp(host, LOCALHOST) || !strcmp(host, rec.hostname) || !strcmp(rec.hostname, WILDCARD)) ; else { xmit.errnum = perm_denied; goto nogrant; } if (ntohs(xmit.grant)) strncpy(host, WILDCARD, MAXSTRLEN - 1); switch (xmit.opcode) { case read_all: if (lu < 0) xmit.errnum = user_n_found; else { #ifndef FreeBSD sprintf(xmit.value, "%s %4.2f %s", rec.user, rec.balance, rec.userinfo); #else snprintf(xmit.value, MAXSTRLEN, "%s %4.2f %s", rec.user, rec.balance, rec.userinfo); #endif } break; case read_balance: if (lu < 0) xmit.errnum = user_n_found; else { #ifndef FreeBSD sprintf(xmit.value, "%4.2f", rec.balance); #else snprintf(xmit.value, MAXSTRLEN, "%4.2f", rec.balance); #endif } break; case read_info: if (lu < 0) xmit.errnum = user_n_found; else strncpy(xmit.value, rec.userinfo, MAXSTRLEN); break; case write_balance: case write_info: if (update(&rec, &xmit, host) < 0) xmit.errnum = inval_data; break; default: xmit.errnum = inval_data; } } nogrant: xmit.errnum = htons(xmit.errnum); if (senddata(newsock, &xmit) < 0) { fprintf(args.logptr, "%s E %-30s %5d send error\n", timestr(), host, port); fflush(args.logptr); close(newsock); continue; } if ((reqbeg == (clock_t) -1) || ((reqend = clock()) == (clock_t) -1)) esecs = -1.0; else esecs = (float) ((int) reqend - (int) reqbeg) / (float) CLOCKS_PER_SEC; xmit.errnum = ntohs(xmit.errnum); fprintf(args.logptr, "%s R %-30s %5d %-16s %1d %1d %5.2f %-20s %-16s %s\n", timestr(), host, port, xmit.ruser, xmit.opcode, xmit.errnum, esecs, xmit.comment, xmit.user, xmit.value); fflush(args.logptr); close(newsock); } } void sighdl ( int sig ) /* ----- sighdl ----- */ { if (sig == SIGALRM) longjmp(env, sig); else if (sig == SIGHUP) { if (args.logptr) { fprintf(args.logptr, "%s I ignoring signal %d\n", timestr(), sig); fflush(args.logptr); } return; } else { if (args.logptr) fprintf(args.logptr, "%s K killed by signal %d\n", timestr(), sig); exit(EX_SOFTWARE); } } char *timestr ( ) /* ----- timestr ----- */ { char *tstr; time_t tloc; if (time(&tloc) == (time_t) -1) { errmsg("time"); exit(EX_SOFTWARE); } tstr = ctime(&tloc); *(tstr + strlen(tstr) - 1) = '\0'; return tstr; } /* ----- update ----- */ int update ( rec_t *rec, xm_t *xmit, char *fromhost ) { char op; double val; if (lookup(xmit->user, rec) < 0) { if (lseek(args.accdesc, 0, SEEK_END) == -1) { errmsg("lseek"); exit(EX_SOFTWARE); } else { memset(rec, 0, sizeof(rec_t)); strncpy(rec->user, xmit->user, MAXUSRLEN - 1); strncpy(rec->hostname, fromhost, MAXSTRLEN - MAXUSRLEN - sizeof(double)); } } switch (xmit->opcode) { case write_balance: if (isdigit(op = *(xmit->value))) rec->balance = atof(xmit->value); else { val = atof(xmit->value + 1); switch (op) { case '+': rec->balance += val; break; case '-': rec->balance -= val; break; case '*': rec->balance *= val; break; case '/': if (val) rec->balance /= val; else return -1; break; default: return -1; break; } } if (write(args.accdesc, rec, sizeof(rec_t)) == -1) { errmsg("write"); exit(EX_SOFTWARE); } if (fsync(args.accdesc) == -1) { errmsg("fsync"); exit(EX_SOFTWARE); } return 0; break; case write_info: strncpy(rec->userinfo, xmit->value, MAXSTRLEN - MAXUSRLEN - sizeof(double) - 1); if (write(args.accdesc, rec, sizeof(rec_t)) == -1) { errmsg("write"); exit(EX_SOFTWARE); } if (fsync(args.accdesc) == -1) { errmsg("fsync"); exit(EX_SOFTWARE); } return 0; break; default: return -1; break; } } void usage ( ) /* ----- usage ----- */ { fprintf(stderr, "\nUsage:\n------\n" "\nMaster Server:\n%s [-d] [-f] " "[-g] [-h[,[,...]]] " "[-l] [-p] -s [-t] " "[-u[,[,...]]]\n", pgm); fprintf(stderr, "\nSlave Server:\n%s [-d] [-g] [-h] [-p] " "[-P] -S [-t]\n", pgm); fprintf(stderr, "\nClient (R):\n%s [-b|-i] [-c] [-r] [-P] " "[-t] [-u]\n", pgm); fprintf(stderr, "\nClient (W):\n%s -b|-i [-c] -w [-P] " "[-t] [-u] \n", pgm); fprintf(stderr, "\nEdit account file:\n%s -e -f\n", pgm); fprintf(stderr, "\nExtract data:\n%s -x|-X -f\n", pgm); } void xmsetup ( xm_t *xmit, char *key ) /* ----- xmsetup ----- */ { char *cr; int lset; time_t secs; union { char byte[sizeof(u_short)]; u_short value; } salt; if (key) { memset(&salt, 0, sizeof salt); salt.byte[0] = random() & 01 ? 'A' + random() % 26 : 'a' + random() % 26; salt.byte[1] = random() & 01 ? 'A' + random() % 26 : 'a' + random() % 26; secs = time(NULL); cr = crypt(key, (char *) &salt.value); strncpy(xmit->key, cr, MAXKEYLEN - 1); xmit->grant = htons(args.grant); xmit->salt = htons(salt.value); xmit->secs = htonl(secs); return; } memset(xmit, 0, sizeof(xm_t)); strncpy(xmit->comment, args.comment, MAXSTRLEN - 1); strncpy(xmit->ruser, pw->pw_name, MAXUSRLEN - 1); strncpy(xmit->user, args.userspec.user, MAXUSRLEN - 1); if (args.read) { if (args.balance && !args.info) xmit->opcode = read_balance; else if (!args.balance && args.info) xmit->opcode = read_info; else xmit->opcode = read_all; } else { strncpy(xmit->value, args.value, MAXSTRLEN - 1); if (args.balance) xmit->opcode = write_balance; else xmit->opcode = write_info; } xmit->opcode = htons(xmit->opcode); } int xtract ( ) /* ----- xtract ----- */ { rec_t rec; if ((args.accdesc = open(args.accfile, O_RDONLY, 0)) == -1) { errmsg("open"); return EX_IOERR; } if (args.xtract == 1) while (read(args.accdesc, &rec, sizeof(rec_t)) == sizeof(rec_t)) printf("%-16s %8.2f %-33s %s\n", rec.user, rec.balance, rec.userinfo, rec.hostname); else while (read(args.accdesc, &rec, sizeof(rec_t)) == sizeof(rec_t)) { printf("%s -u %s -w -b %8.2f\n", pgm, rec.user, rec.balance); printf("%s -u %s -w -i %s\n", pgm, rec.user, rec.userinfo); } close(args.accdesc); return 0; } /* ----- main ----- */ int main ( int argc, char *argv[] ) { char ch; char *pos; FILE *pipe; if (!(pgm = strrchr(argv[0], '/'))) pgm = argv[0]; else pgm++; if (argc == 2 && !strcmp(argv[1], "usage")) { usage(); return EX_USAGE; } if (!(pw = getpwuid(getuid()))) { errmsg("getpwuid"); return EX_OSERR; } memset(&args, 0, sizeof args); strncpy(args.accfile, ACCFILE, MAXSTRLEN - 1); strncpy(args.hostspec.hostname, LOCALHOST, MAXSTRLEN - 1); strncpy(args.logfile, LOGFILE, MAXSTRLEN - 1); args.accdesc = -1; args.mport = MPORT; args.sport = SPORT; args.timeout = TIMEOUT; strncpy(args.userspec.user, pw->pw_name, MAXUSRLEN - 1); while ((ch = getopt(argc, argv, "bc:def:gh:il:p:P:rsSt:u:wxX")) != EOF) switch (ch) { case 'b': args.balance = 1; break; case 'c': strncpy(args.comment, optarg, MAXSTRLEN - 1); break; case 'd': args.daemon = 1; break; case 'e': args.edit = 1; break; case 'f': strncpy(args.accfile, optarg, MAXSTRLEN - 1); break; case 'g': args.grant = 1; break; case 'h': strncpy(args.hostspec.hostname, optarg, MAXSTRLEN - 1); break; case 'i': args.info = 1; break; case 'l': strncpy(args.logfile, optarg, MAXSTRLEN - 1); break; case 'p': args.mport = atoi(optarg); break; case 'P': args.sport = atoi(optarg); break; case 'r': args.read = 1; break; case 's': args.server = master; break; case 'S': args.server = slave; break; case 't': args.timeout = atoi(optarg); break; case 'u': strncpy(args.userspec.user, optarg, MAXSTRLEN - 1); break; case 'w': args.write = 1; break; case 'x': args.xtract = 1; break; case 'X': args.xtract = 2; break; default: return EX_USAGE; break; } if (argv[optind]) strncpy(args.value, argv[optind], MAXSTRLEN - 1); if (args.edit) return edit(); if (args.xtract) return xtract(); if (time(&seed) == (time_t) -1) { errmsg("time"); return EX_SOFTWARE; } else srandom(seed); if (!args.write) args.read = 1; else { args.read = 0; if ((args.balance && args.info) || (!args.balance && !args.info)) { usage(); return EX_USAGE; } } if (!args.server) return client(); else return server(); }