/* * 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. * * Copyright (C) 2000-2002 * Andrey Aristarkhov . * All rights reserved * Partial Copyright (C) 1996 * David L. Nugent. All rights reserved. * * $Source: /home/dron/CVS/misc/cvspasswd/user.c,v $ * $Id: user.c,v 1.2 2002/08/12 17:46:54 dron Exp $ * * Frontend functions for CVS' passwords manupulation * */ #include "user.h" #include "cvs.h" static const char ident[] = "$Id: user.c,v 1.2 2002/08/12 17:46:54 dron Exp $"; static const char rcsid[] = "$Header: /home/dron/CVS/misc/cvspasswd/user.c,v 1.2 2002/08/12 17:46:54 dron Exp $"; #ifdef HAVE_GETPASSPHRASE #define GETPASS getpassphrase #else #define GETPASS getpass #endif #define DL error(0,0,"\t%s: %d",__FILE__,__LINE__) static char pathpwd[MAXPATHLEN]; static char * pwpath = pathpwd; static int extendline(char **buf, int * buflen, int needed) { if (needed > *buflen) { char *tmp = realloc(*buf, needed); if (tmp == NULL) return -1; *buf = tmp; *buflen = needed; } return *buflen; } static int extendarray(char ***buf, int * buflen, int needed) { if (needed > *buflen) { char **tmp = realloc(*buf, needed * sizeof(char *)); if (tmp == NULL) return -1; *buf = tmp; *buflen = needed; } return *buflen; } static int pw_fileupdate(char const * filename, mode_t fmode, char const * newline, char const * prefix, int pfxlen, int updmode) { int rc = 0; int updated = PWD_CREATE; int linesize = PWBUFSZ; char *line; FILE *outfp; FILE *infp; int outfd; char file[MAXPATHLEN]; int infd; if (pfxlen <= 1) rc = EINVAL; else { infd = open(filename, O_RDWR | O_CREAT, fmode); if (infd == -1) rc = errno; else { infp = fdopen(infd, "r+"); if (infp == NULL) { rc = errno; /* Assumes fopen(3) sets errno from open(2) */ close(infd); } else { strcpy(file, filename); strcat(file, ".new"); #ifdef __FreeBSD__ outfd = open(file, O_RDWR | O_CREAT | O_TRUNC | O_EXLOCK, fmode); #else outfd = open(file, O_RDWR | O_CREAT | O_TRUNC , fmode); #endif if (outfd == -1) rc = errno; else { outfp = fdopen(outfd, "w+"); if (outfp == NULL) { rc = errno; close(outfd); } else { updated = PWD_CREATE; linesize = PWBUFSZ; line = malloc(linesize); nextline: while (fgets(line, linesize, infp) != NULL) { char *p = strchr(line, '\n'); while ((p = strchr(line, '\n')) == NULL) { int l; if (extendline(&line, &linesize, linesize + PWBUFSZ) == -1) { int ch; fputs(line, outfp); while ((ch = fgetc(infp)) != EOF) { fputc(ch, outfp); if (ch == '\n') break; } goto nextline; } l = strlen(line); if (fgets(line + l, linesize - l, infp) == NULL) break; } if (*line != '#' && *line != '\n') { if (!updated && strncmp(line, prefix, pfxlen) == 0) { updated = updmode == PWD_UPDATE ? PWD_UPDATE : PWD_DELETE; /* * Only actually write changes if updating */ if (updmode == PWD_UPDATE) strcpy(line, newline); else if (updmode == PWD_DELETE) continue; } } fputs(line, outfp); } /* * Now, we need to decide what to do: If we are in * update mode, and no record was updated, then error If * we are in insert mode, and record already exists, * then error */ if (updmode != updated) /* -1 return means: * update,delete=no user entry * create=entry exists */ rc = -1; else { /* * If adding a new record, append it to the end */ if (updmode == PWD_CREATE) fputs(newline, outfp); /* * Flush the file and check for the result */ if (fflush(outfp) == EOF) rc = errno; /* Failed to update */ else { /* * Copy data back into the * original file and truncate */ rewind(infp); rewind(outfp); while (fgets(line, linesize, outfp) != NULL) fputs(line, infp); /* * If there was a problem with copying * we will just rename 'file.new' * to 'file'. * This is a gross hack, but we may have * corrupted the original file * Unfortunately, it will lose the inode * and hence the lock. */ if (fflush(infp) == EOF || ferror(infp)) rename(file, filename); else ftruncate(infd, ftell(infp)); } } free(line); fclose(outfp); } remove(file); } fclose(infp); } } } return rc; } /* 0 - it's ok otherwise errno */ static int setpwpath(const char * file) { int res = open(file,O_RDWR); if (res == -1) { return errno; } close(res); strcpy(pathpwd,file); return 0; } static char * getpwpath() { static char pathbuf[MAXPATHLEN]; strcpy(pathbuf,pathpwd); return pathbuf; } /* * Return values: * -1 -- generic error * 0 -- success * 1 -- user is empty or NULL * 2 -- passwd is empty or NULL */ static int fmtpwentry(char **buf, const char *user, const char * passwd, const char * alias) { int l; int has_alias; char *pw; if (user == NULL || strlen(user) == 0) { return 1; } else if (passwd == NULL || strlen(passwd) == 0) { return 2; } if (alias == NULL || strlen(alias) == 0) { has_alias = 0; } else { has_alias = strlen(alias); } l = strlen(passwd)+strlen(user) +has_alias+ 2; pw = (char *)malloc(l); if (has_alias) { sprintf(pw,"%s:%s:%s\n",user,passwd,alias); } else { sprintf(pw,"%s:%s\n",user,passwd); } *buf = pw; return 0; } static int pw_update(const char * user, const char * passwd, const char * alias, int mode) { int rc = 0; char pfx[32]; char * pwbuf; int l = sprintf(pfx, "%s:", user); /* * Update passwd file */ if (mode != PWD_DELETE) { rc = fmtpwentry(&pwbuf,user,passwd,alias); #ifdef _DEBUG printf("Return from fmtpwentry: %d\n",rc); #endif if (rc != 0) { return rc; } } else { pwbuf = malloc(strlen(user)+2); sprintf(pwbuf,"%s:",user); } rc = pw_fileupdate(getpwpath(), 0644, pwbuf, pfx, l, mode); if (pwbuf != NULL) { free(pwbuf); } return rc; } #ifndef CHARSET_EBCDIC #define LF 10 #define CR 13 #else /*CHARSET_EBCDIC*/ #define LF '\n' #define CR '\r' #endif /*CHARSET_EBCDIC*/ static int getline(char *s, int n, FILE *f) { register int i = 0; while (1) { s[i] = (char) fgetc(f); if (s[i] == CR) { s[i] = fgetc(f); } if ((s[i] == 0x4) || (s[i] == LF) || (i == (n - 1))) { s[i] = '\0'; return (feof(f) ? 1 : 0); } ++i; } } #define MAX_STRING_LEN 256 static char * get_password(const char * user) { static char pwd[MAX_STRING_LEN]; char line[MAX_STRING_LEN]; FILE * fpw=NULL; char * token; fpw = fopen (getpwpath(), "r"); if (fpw == NULL) { return NULL; } memset(pwd,0,MAX_STRING_LEN); while (!(getline (line, MAX_STRING_LEN, fpw))) { if (line[0] == '#') { continue; } token = strtok(line,":"); if (token == NULL) { continue; } if (strcmp (user, token) != 0) { continue; } /* User found */ token = strtok(NULL,":"); strcpy(pwd,token); fclose(fpw); return pwd; } fclose(fpw); return NULL; } static char * get_alias(const char * user) { static char alias[MAX_STRING_LEN]; char line[MAX_STRING_LEN]; FILE * fpw; char * token; fpw = fopen (getpwpath(), "r"); while (!(getline (line, sizeof (line), fpw))) { if (line[0] == '#') { continue; } token = strtok(line,":"); if (strcmp (user, token) != 0) { continue; } /* User found */ token = strtok(NULL,":"); token = strtok(NULL,":"); if (token == NULL) { return NULL; } strcpy(alias,token); fclose(fpw); return alias; } fclose(fpw); return NULL; } /*extern char * crypt(const char *, const char *);*/ static char const chars[] = "0123456789abcdefghijklmnopqrstuvwxyzABCDEFGHIJKLMNOPQRSTUVWXYZ."; static char * pw_crypt(const char *password) { int i; char salt[12]; static char buf[256]; /* * Calculate a salt value */ #ifdef __FreeBSD__ srandomdev(); #else srandom((unsigned long) (time(NULL) ^ getpid())); #endif for (i = 0; i < 8; i++) salt[i] = chars[random() % 63]; salt[i] = '\0'; return strcpy(buf, crypt(password, salt)); } /* * Return values: * -1 - user exists * 0 - user added, otherwise another code */ static int pw_add_user(const char * user, const char * passwd, const char * alias) { char * newpasswd; int res; if (get_password(user) != NULL) { return -1; } if (passwd == NULL || strlen(passwd) == 0) { newpasswd=strdup("*"); } else { newpasswd = strdup(pw_crypt(passwd)); } res = pw_update(user,newpasswd,alias,PWD_CREATE); free(newpasswd); return res; } /* -1 - user does not exists * -2 - no permission */ static int pw_mod_user(const char * user, const char * passwd, const char * alias) { char * newpasswd; if ((newpasswd = get_password(user)) == NULL) { return -1; } if (passwd != NULL) { newpasswd = pw_crypt(passwd); } if (alias == NULL) alias = get_alias(user); return pw_update(user,newpasswd,alias,PWD_UPDATE); } /* -1 - user does not exists */ static int pw_del_user(const char * user) { if (get_password(user) == NULL) { return -1; } return pw_update(user,NULL,NULL,PWD_DELETE); } static const char *const password_usage[] = { "Usage: %s %s [username]\n", "\tIf no \"username\" is given password will be set for the current user\n", "\t\"username\"\tUse it if you want to change password for the specified user\n" "(Specify the --help global option for a list of other help options)\n", NULL }; static const char *const user_usage[] = { "Usage: %s %s <[-a | -m | -d] username> [-u alias] [-p | -P password]\n", "\t-a|-m|-d\t'add', 'modify' or 'delete' user respectively\n", "\t-u\tUse \"alias\" to specify system user for cvs-user.\n", "\t-P\tUse \"password\" to specify user password in a command line OR\n", "\t-p\tenter user password interactively\n", "(Specify the --help global option for a list of other help options)\n", NULL }; /* * Returns current user for passwdoed operation * Parameter - user name specified in -a, -m or -d options */ char * get_current_user(const char * user) { return NULL; } enum pwd_read_mode { PRM_NEW, PRM_ADMIN, PRM_CHECK }; static void verify_password_phrase(const char * password, unsigned char complete_check) { int plen, i; if (!password) { error(1,0,"Failed to read password"); } if (!complete_check) return; plen = strlen(password); for (i=0;i 126) { error(1,0,"Invalid character in password (0x%x). Password can contain only printable characters.",password[i]); } } if (plen < MIN_PASSWORD_LEN) { error(1,0,"Password length can't be less than %d characters",MIN_PASSWORD_LEN); } if (plen > MAX_PASSWORD_LEN) { error(1,0,"Password length can't be more than %d characters",MAX_PASSWORD_LEN); } } /* * Reads and verifies user's password from an input * Return password for the user */ #define safe_string(a) a ? a : "NULL" static char * read_user_password(const char * user, int pwd_read_mode) { char prompt[128]; char * pwd, * pwd_verify; static char password[_PASSWORD_LEN]; memset(password,0,_PASSWORD_LEN); switch (pwd_read_mode) { case PRM_NEW: sprintf(prompt,"Enter New password for user '%s':",user); /* We need to strdup because GETPASS uses static object */ pwd_verify = GETPASS(prompt); verify_password_phrase(pwd_verify,TRUE); pwd = strdup(pwd_verify); memset(pwd_verify,0,strlen(pwd_verify)); sprintf(prompt,"Re-type New password for user '%s':",user); pwd_verify = GETPASS(prompt); verify_password_phrase(pwd_verify,FALSE); if (strcmp(pwd,pwd_verify)) { free(pwd); error(1,0,"Passwords are different"); } else { free(pwd); strcpy(password,pwd_verify); memset(pwd_verify,0,strlen(pwd_verify)); } break; case PRM_CHECK: pwd = get_password(user); sprintf(prompt,"Enter current password for user '%s': ",user); pwd_verify = GETPASS(prompt); verify_password_phrase(pwd_verify,FALSE); if (strcmp(pwd,crypt(pwd_verify,pwd))==0) { strcpy(password,pwd); } else { error(1,0,"Wrong password",user); } break; case PRM_ADMIN: pwd = get_password(CVS_ADMIN_USER); if (pwd == NULL) error(1,0,"Administrator's user accout not found. Please contact your CVS admin."); if (strcmp(pwd,"*")==0) error(1,0,"Password for Administrator's user accout is not set. Please contact your CVS admin."); pwd_verify = GETPASS("Enter CVS Administrator password: "); verify_password_phrase(pwd_verify,FALSE); if (strcmp(pwd,crypt(pwd_verify,pwd))==0) { strcpy(password,pwd); } else { error(1,0,"Administrator password is invalid"); } break; } return password; } static void new_user(const char * user, const char * password, const char * alias, cvsroot_t * root) { char * pwd; if (get_password(user)!=NULL) { error(1,0,"User '%s' already exists",user); } if ( (root->username != NULL && strcmp(root->username, CVS_ADMIN_USER)) || root->username == NULL) { error(0,0,"You are not an administrator"); read_user_password(NULL,PRM_ADMIN); } /* It's Ok. User has administrator privelegies */ if (password == NULL) { pwd = read_user_password(user,PRM_NEW); } if (pw_add_user(user,pwd,alias) != 0) { error(1,0,"Can't add user '%s' - internal error",user); } error(0,0,"User '%s' successefully added",user); } static void modify_user(const char * user, const char * password, const char * alias, cvsroot_t * root, unsigned char update_password) { char * pwd; pwd = NULL; if (get_password(user)==NULL) { error(1,0,"User '%s' not found",user); } /* User alias could be changed only by administrator */ if (alias != NULL && ((root->username != NULL && strcmp(root->username, CVS_ADMIN_USER)) || root->username == NULL)) { error(0,0,"You are not allowed to change alias for user '%s'",user); pwd = read_user_password(NULL,PRM_ADMIN); /* It's Ok. User has administrator privelegies */ } if (!pwd) { /* Possibly admin password in pwd */ read_user_password(user,PRM_CHECK); } /* It's Ok. User has administrator privelegies or changes his own password */ pwd = NULL; if (update_password) { if (password == NULL) { pwd = read_user_password(user,PRM_NEW); } else { pwd = (char *)password; } } pw_mod_user(user,update_password ? pwd : NULL,alias); error(0,0,"Password for user '%s' successfully changed",user); } static void set_password_path(cvsroot_t * root) { char passwdFile[MAXPATHLEN]; sprintf(passwdFile,"%s/%s/%s",root->directory,CVSROOTADM,CVSROOTADM_PASSWD); setpwpath(passwdFile); } int password(int argc, char ** argv) { char * user; user = NULL; if (argc > 3 || argc == -1) { usage(password_usage); } error(0,0,"Argc: %d",argc); if (argc > 1) { user = argv[1]; } if (user == NULL) user = current_parsed_root->username; if (user==NULL) { error(1,0,"Can't detect current user. Specify username explictly."); } set_password_path(current_parsed_root); modify_user(user,NULL,NULL,current_parsed_root,1 /* update password */); } #define checkOper(op) if (##op!= PWD_UNDEFINED) {\ error(0,0,"Choose one of of options -a | -m | -d");\ usage(user_usage);\ } int user(int argc, char ** argv) { enum updtype op; char * user, * passwd, * alias; char * cvsroot; int res; char c; unsigned char hasPassword, needPassword; user = alias = passwd = NULL; op = PWD_UNDEFINED; hasPassword = needPassword = 0; /* parse args */ if (argc == -1) usage(user_usage); optind = 0; while ((c = getopt (argc, argv, "+a:d:m:u:pP:")) != -1) { switch (c) { case 'a': checkOper(op); op = PWD_CREATE; user = optarg; break; case 'm': checkOper(op); op = PWD_UPDATE; user = optarg; break; case 'd': checkOper(op); op = PWD_DELETE; user = optarg; break; case 'u': alias = optarg; break; case 'P': hasPassword = 1; passwd = optarg; break; case 'p': needPassword = 1; break; case '?': default: usage (user_usage); break; } } argc -= optind; argv += optind; if (op == PWD_UNDEFINED) { usage(user_usage); } if (hasPassword && needPassword) { error(0,0,"-p and -P options are mutually exclusive"); usage(user_usage); } /* end of parse args */ set_password_path(current_parsed_root); error(0,0,"File: %s, User: %s(%s), Alias: %s", getpwpath(), safe_string(current_parsed_root->username), getcaller(), safe_string(alias)); switch (op) { case PWD_CREATE: new_user(user,passwd,alias,current_parsed_root); return 0; case PWD_UPDATE: modify_user(user,passwd,alias,current_parsed_root,hasPassword | needPassword); return 0; case PWD_DELETE: if ( (current_parsed_root->username!=NULL && strcmp(current_parsed_root->username,CVS_ADMIN_USER)) || current_parsed_root->username==NULL ) { read_user_password(NULL,PRM_ADMIN); } return pw_del_user(user); } return 0; }