Hi All! Here my implementation of "user" & "pass(word)" commands. Patches to other files in "src" directory also applied.
Regards, Andrey Aristarkhov BiTechnology
cvs.h.diff
Description: Binary data
main.c.diff
Description: Binary data
Makefile.in.diff
Description: Binary data
/*
* 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 <[EMAIL PROTECTED]>.
* 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<plen;i++) {
if ((unsigned char)password[i] < 33 || (unsigned char)password[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;
}
/*
* 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 <[EMAIL PROTECTED]>.
* All rights reserved
* Partial Copyright (C) 1996
* David L. Nugent. All rights reserved.
*
* $Source: /home/dron/CVS/misc/cvspasswd/user.h,v $
* $Id: user.h,v 1.1 2002/08/12 16:31:29 dron Exp $
*
* Header file for user & password file manipulation functions
* within CVS repository
*/
#ifndef CVS_USER_H_
#define CVS_USER_H_
#include <sys/types.h>
#include <pwd.h>
#include <grp.h>
#ifdef OS_FREEBSD
#include <sys/cdefs.h>
#endif
#ifdef __cplusplus
extern "C" {
#endif
int password (int argc, char **argv);
int user (int argc, char **argv);
enum updtype { PWD_CREATE, PWD_UPDATE, PWD_DELETE, PWD_UNDEFINED };
#define CVS_ADMIN_USER "admin"
#define PWBUFSZ 1024
#ifndef _PASSWORD_LEN
#define _PASSSWORD_LEN
#endif
#define MAX_PASSWORD_LEN _PASSWORD_LEN
#define MIN_PASSWORD_LEN 6
#ifndef TRUE
#define TRUE 1
#endif
#ifndef FALSE
#define FALSE 0
#endif
#ifdef __cplucplus
}
#endif
#endif /* CVS_USER_H */
