1 #pragma ident   "%Z%%M% %I%     %E% SMI"
   2 
   3 /**************************************************************************** 
   4 
   5    Copyright (c) 1999,2000 WU-FTPD Development Group. 
   6    All rights reserved.
   7    
   8    Portions Copyright (c) 1980, 1985, 1988, 1989, 1990, 1991, 1993, 1994 
   9    The Regents of the University of California.  Portions Copyright (c) 
  10    1993, 1994 Washington University in Saint Louis.  Portions Copyright 
  11    (c) 1996, 1998 Berkeley Software Design, Inc.  Portions Copyright (c) 
  12    1998 Sendmail, Inc.  Portions Copyright (c) 1983, 1995, 1996, 1997 Eric 
  13    P. Allman.  Portions Copyright (c) 1989 Massachusetts Institute of 
  14    Technology.  Portions Copyright (c) 1997 by Stan Barber.  Portions 
  15    Copyright (C) 1991, 1992, 1993, 1994, 1995, 1996, 1997 Free Software 
  16    Foundation, Inc.  Portions Copyright (c) 1997 by Kent Landfield. 
  17  
  18    Use and distribution of this software and its source code are governed 
  19    by the terms and conditions of the WU-FTPD Software License ("LICENSE"). 
  20  
  21    $Id: privatepw.c,v 1.10 2000/07/01 18:43:59 wuftpd Exp $
  22  
  23 ****************************************************************************/
  24 /*
  25    Subsystem:  WU-FTPD FTP Server
  26    Purpose:    Change WU-FTPD Guest Passwords
  27    File Name:  privatepw.c               
  28 
  29    usage: privatepw [-c] [-f passwordfile] [-g group] accessgroup
  30    privatepw [-d] [-f passwordfile] accessgroup
  31    privatepw [-l] [-f passwordfile] 
  32    -c:           creates a new file.
  33    -d:           deletes specified accessgroup.
  34    -l:           list contents of ftpgroups file.
  35    -f ftpgroups: updates the specified file.
  36    -g group:     set real group to the specified group.
  37 
  38    This software was initially written by Kent Landfield (kent@landfield.com)
  39  */
  40 
  41 #include <sys/types.h>
  42 #include <sys/signal.h>
  43 #include <sys/stat.h>
  44 #include <string.h>
  45 #include <stdio.h>
  46 #include <stdlib.h>
  47 #include <time.h>
  48 #include <grp.h>
  49 #include <unistd.h>
  50 #include "config.h"
  51 #include "pathnames.h"
  52 
  53 #define BUFLEN 256
  54 #define GROUPLEN 8
  55 
  56 char *tmp;
  57 char line[BUFLEN];
  58 FILE *fp;
  59 int verbose = 0;
  60 
  61 static unsigned char itoa64[] = /* 0 ... 63 => ascii - 64 */
  62 "./0123456789ABCDEFGHIJKLMNOPQRSTUVWXYZabcdefghijklmnopqrstuvwxyz";
  63 
  64 void print_copyright(void);
  65 
  66 static void usage(void)
  67 {
  68     fprintf(stderr, "usage: privatepw [-c] [-f ftpgroups] [-g group] accessgroup\n");
  69     fprintf(stderr, "       privatepw [-d] [-f ftpgroups] accessgroup\n");
  70     fprintf(stderr, "       privatepw [-l] [-f ftpgroups]\n");
  71     fprintf(stderr, "\t\t-c:           creates a new file.\n");
  72     fprintf(stderr, "\t\t-d:           deletes specified accessgroup.\n");
  73     fprintf(stderr, "\t\t-l:           list contents of ftpgroups file.\n");
  74     fprintf(stderr, "\t\t-f ftpgroups: updates the specified file.\n");
  75     fprintf(stderr, "\t\t-g group:     set real group to the specified group.\n");
  76     exit(1);
  77 }
  78 
  79 static void to64(register char *s, register long v, register int n)
  80 {
  81     while (--n >= 0) {
  82         *s++ = itoa64[v & 0x3f];
  83         v >>= 6;
  84     }
  85 }
  86 
  87 static void terminate(void)
  88 {
  89     if (tmp)
  90         unlink(tmp);
  91     exit(1);
  92 }
  93 
  94 static void catchintr(void)
  95 {
  96     fprintf(stderr, "Interrupted.\n");
  97     terminate();
  98 }
  99 
 100 static char *savit(char *s)
 101 {
 102     char *d;
 103 
 104     if ((d = (char *) malloc(strlen(s) + 1)) == NULL) {
 105         fprintf(stderr, "Whoa... Malloc failed.\n");
 106         terminate();
 107     }
 108     strcpy(d, s);
 109     return (d);
 110 }
 111 
 112 static int confirmed(char *accessgroup)
 113 {
 114     register int ch;
 115 
 116     printf("Delete %s: Are your sure ? (y/n) ", accessgroup);
 117     ch = getc(stdin);
 118     if (ch == 'y')
 119         return (1);
 120     return (0);
 121 }
 122 
 123 static char *getgroup(char *msg)
 124 {
 125     register int ch;
 126     register char *p;
 127     static char buf[GROUPLEN + 1];
 128 
 129     fputs(msg, stderr);
 130     rewind(stderr);             /* implied flush */
 131     for (p = buf; (ch = getc(stdin)) != EOF && ch != '\n';)
 132         if (p < buf + GROUPLEN)
 133             *p++ = ch;
 134     *p = '\0';
 135 
 136     if (getgrnam(buf) == NULL) {
 137         fprintf(stderr, "Invalid group \'%s\' specified\n", buf);
 138         terminate();
 139     }
 140     return (buf);
 141 }
 142 
 143 static void addrecord(char *accessgroup, char *sysgroup, char *msg, FILE *f)
 144 {
 145     char *pw, *cpw, salt[3];
 146 #ifndef NO_CRYPT_PROTO
 147     extern char *crypt(const char *, const char *);
 148 #endif
 149     char *getpass(const char *prompt);
 150 
 151     printf("%s %s\n", msg, accessgroup);
 152 
 153     if (sysgroup[0] == '\0')
 154         strcpy(sysgroup, getgroup("Real System Group to use: "));
 155 
 156     pw = savit((char *) getpass("New password: "));
 157     if (strcmp(pw, (char *) getpass("Re-type new password: "))) {
 158         fprintf(stderr, "They don't match, sorry.\n");
 159         if (tmp)
 160             unlink(tmp);
 161         exit(1);
 162     }
 163 
 164     srand((int) time((time_t *) NULL));
 165     to64(&salt[0], rand(), 2);
 166     cpw = crypt(pw, salt);
 167     free(pw);
 168     fprintf(f, "%s:%s:%s\n", accessgroup, cpw, sysgroup);
 169 }
 170 
 171 static void list_privatefile(char *privatefile)
 172 {
 173     if (verbose)
 174         fprintf(stderr, "Private File: %s file.\n", privatefile);
 175 
 176     if ((fp = fopen(privatefile, "r")) == NULL) {
 177         fprintf(stderr, "Could not open %s file.\n", privatefile);
 178         exit(1);
 179     }
 180 
 181     printf("\nWU-FTPD Private file: %s\n", privatefile);
 182     printf("accessgroup : password : system group\n");
 183     printf("-------\n");
 184 
 185     while (fgets(line, BUFLEN, fp) != NULL)
 186         fputs(line, stdout);
 187     printf("-------\n");
 188 }
 189 
 190 int main(int argc, char **argv)
 191 {
 192     extern void (*signal(int sig, void (*disp) (int))) (int);
 193     extern int getopt(int argc, char *const *argv, const char *optstring);
 194     extern char *optarg;
 195     extern int optind;
 196     extern int opterr;
 197 
 198     struct stat stbuf;
 199 
 200     char realgroup[BUFLEN];
 201     char *passwdpath;
 202     char *cp;
 203 
 204     char accessgroup[BUFLEN];
 205     char w[BUFLEN];
 206     char command[BUFLEN];
 207 
 208     int create;
 209     int delete;
 210     int list;
 211     int found;
 212     int lineno;
 213     int c;
 214 
 215     FILE *tfp;
 216 
 217 #ifdef HAVE_MKSTEMP
 218     char tmpname[BUFLEN];
 219     int tfd;
 220 #endif
 221 
 222     opterr = 0;
 223     create = 0;
 224     delete = 0;
 225     list = 0;
 226 
 227     tmp = NULL;
 228     realgroup[0] = '\0';
 229 
 230     passwdpath = _PATH_PRIVATE;
 231 
 232     if (argc == 1)
 233         usage();
 234 
 235     while ((c = getopt(argc, argv, "Vvcdf:g:l")) != EOF) {
 236         switch (c) {
 237         case 'd':
 238             delete++;
 239             break;
 240         case 'c':
 241             create++;
 242             break;
 243         case 'f':
 244             passwdpath = optarg;
 245             break;
 246         case 'g':
 247             strcpy(realgroup, optarg);
 248             if (getgrnam(realgroup) == NULL) {
 249                 fprintf(stderr, "Invalid group \'%s\' specified\n", realgroup);
 250                 return (1);
 251             }
 252             break;
 253         case 'l':
 254             list++;
 255             break;
 256         case 'v':
 257             verbose++;
 258             break;
 259         case 'V':
 260             print_copyright();
 261             return (0);
 262             /* NOTREACHED */
 263         default:
 264             usage();
 265         }
 266     }
 267 
 268     if (list) {
 269         list_privatefile(passwdpath);
 270         return (0);
 271     }
 272 
 273     if (optind >= argc) {
 274         fprintf(stderr, "Need to specify an accessgroup name.\n");
 275         usage();
 276     }
 277 
 278     signal(SIGINT, (void (*)()) catchintr);
 279 
 280     strcpy(accessgroup, argv[optind]);
 281 
 282     if (create) {
 283         if (stat(passwdpath, &stbuf) == 0) {
 284             fprintf(stderr, "%s exists, cannot create it.\n", passwdpath);
 285             fprintf(stderr, "Remove -c option or use the -f option to specify another.\n");
 286             return (1);
 287         }
 288 
 289         if ((tfp = fopen(passwdpath, "w")) == NULL) {
 290             fprintf(stderr, "Could not open \"%s\" for writing.\n", passwdpath);
 291             perror("fopen");
 292             return (1);
 293         }
 294 
 295         tmp = passwdpath;
 296 
 297         printf("Creating WU-FTPD Private file: %s\n", passwdpath);
 298         addrecord(accessgroup, realgroup, "Adding accessgroup", tfp);
 299 
 300         fclose(tfp);
 301         return (0);
 302     }
 303 
 304 #ifdef HAVE_MKSTEMP
 305     strcpy (tmpname, "/tmp/privatepwXXXXXX");
 306     tmp = tmpname;
 307     if ((tfd = mkstemp(tmp)) < 0) {
 308         fprintf(stderr, "Could not open temp file.\n");
 309         return (1);
 310     }
 311 
 312     if ((tfp = fdopen(tfd, "w")) == NULL) {
 313         unlink(tmp);
 314         fprintf(stderr, "Could not open temp file.\n");
 315         return (1);
 316     }
 317 #else
 318     tmp = tmpnam(NULL);
 319 
 320     if ((tfp = fopen(tmp, "w")) == NULL) {
 321         fprintf(stderr, "Could not open temp file.\n");
 322         return (1);
 323     }
 324 #endif
 325 
 326     if ((fp = fopen(passwdpath, "r")) == NULL) {
 327         fprintf(stderr, "Could not open %s file.\n", passwdpath);
 328         fprintf(stderr, "Use -c option to create new one.\n");
 329         return (1);
 330     }
 331 
 332     lineno = 0;
 333     found = 0;
 334 
 335     while (fgets(line, BUFLEN, fp) != NULL) {
 336         lineno++;
 337 
 338         if (found || (line[0] == '#') || (!line[0])) {
 339             fputs(line, tfp);
 340             continue;
 341         }
 342 
 343         strcpy(w, line);
 344 
 345         if ((cp = strchr(w, ':')) == NULL) {
 346             fprintf(stderr, "%s: line %d: invalid record format.\n", passwdpath, lineno);
 347             continue;
 348         }
 349         *cp++ = '\0';
 350 
 351         if ((cp = strchr(cp, ':')) == NULL) {
 352             fprintf(stderr, "%s: line %d: invalid record format.\n", passwdpath, lineno);
 353             continue;
 354         }
 355         *cp++ = '\0';
 356 
 357         if (strcmp(accessgroup, w)) {
 358             fputs(line, tfp);
 359             continue;
 360         }
 361         else {
 362             if (delete) {
 363                 if (!confirmed(accessgroup))
 364                     terminate();
 365             }
 366             else {
 367                 if (realgroup[0] == '\0') {
 368                     strcpy(realgroup, cp);
 369                     if ((cp = strchr(realgroup, '\n')) != NULL)
 370                         *cp = '\0';
 371                 }
 372                 addrecord(accessgroup, realgroup, "Updating accessgroup", tfp);
 373             }
 374             found = 1;
 375         }
 376     }
 377 
 378     if (!found && !delete)
 379         addrecord(accessgroup, realgroup, "Adding accessgroup", tfp);
 380     else if (!found && delete) {
 381         fprintf(stderr, "%s not found in %s.\n", accessgroup, passwdpath);
 382         terminate();
 383     }
 384 
 385     fclose(fp);
 386     fclose(tfp);
 387 
 388     sprintf(command, "cp %s %s", tmp, passwdpath);
 389     system(command);
 390     unlink(tmp);
 391     return (0);
 392 }