/*
* Copyright (c) 1992, Brian Berliner and Jeff Polk
* Copyright (c) 1989-1992, Brian Berliner
*
* You may distribute under the terms of the GNU General Public License as
* specified in the README file that comes with the CVS 1.4 kit.
*
* passwd
*
* Changes the password of the caller
*
* Corey Minyard: Initial development
* Tony Hoyle: 23/11/2001 Rewrite for cvsnt
*
*/
#include "cvs.h"
#include "getline.h"
#if defined(_WIN32) && defined(SERVER_SUPPORT)
/* We use this for sanity checking */
extern int isDomainMember();
#endif
extern char *crypt (const char *, const char *);
#define ascii_to_bin(c) ((c)>='a'?(c-59):(c)>='A'?((c)-53):(c)-'.')
#define bin_to_ascii(c) ((c)>=38?((c)-38+'a'):(c)>=12?((c)-12+'A'):(c)+'.')
static passwd_entry *passwd_list = NULL;
static int passwd_list_size = 0;
char *crypt_password(char *typed_password)
{
time_t tm;
char salt[2];
if(typed_password)
{
time(&tm);
salt[0] = bin_to_ascii(tm & 0x3f);
salt[1] = bin_to_ascii((tm >> 5) & 0x3f);
strcpy(typed_password,crypt(typed_password, salt));
}
return typed_password;
}
void free_passwd_list()
{
if(passwd_list)
{
int u;
for(u=0; u<passwd_list_size; u++)
{
xfree(passwd_list[u].username);
xfree(passwd_list[u].password);
xfree(passwd_list[u].real_username);
}
xfree(passwd_list);
}
}
void init_passwd_list()
{
if(passwd_list)
free_passwd_list();
passwd_list = NULL;
passwd_list_size = 0;
}
passwd_entry *new_passwd_entry()
{
passwd_list_size++;
passwd_list = (passwd_entry*)xrealloc(passwd_list,sizeof(passwd_entry)*passwd_list_size);
memset(passwd_list+passwd_list_size-1,0,sizeof(passwd_entry));
return passwd_list+passwd_list_size-1;
}
passwd_entry *find_passwd_entry(const char *username)
{
int u;
for(u=0; u<passwd_list_size; u++)
if(passwd_list[u].username && !usercmp(passwd_list[u].username,username))
return passwd_list+u;
return NULL;
}
int does_passwd_user_exist(const char *username)
{
if(find_passwd_entry(username))
return 1;
if(system_auth && getpwnam(username))
return 1;
return 0;
}
static int write_passwd_entry(FILE *fp, int entry_num)
{
if(!passwd_list[entry_num].username)
return 0;
fprintf(fp,"%s",passwd_list[entry_num].username);
if(passwd_list[entry_num].password)
fprintf(fp,":%s",passwd_list[entry_num].password);
else
fprintf(fp,":");
if(passwd_list[entry_num].real_username)
fprintf(fp,":%s",passwd_list[entry_num].real_username);
fprintf(fp,"\n");
return 0;
}
int read_passwd_list()
{
char *filename;
FILE *fp;
char *linebuf = NULL;
size_t linebuf_len;
passwd_entry *passnode;
filename = (char*)xmalloc(strlen(current_parsed_root->directory)
+ strlen("/CVSROOT/passwd")
+ 1);
strcpy (filename, current_parsed_root->directory);
strcat (filename, "/CVSROOT/passwd");
init_passwd_list();
fp = CVS_FOPEN (filename, "r");
if (fp != NULL)
{
while (getline (&linebuf, &linebuf_len, fp) >= 0)
{
char *user;
if(linebuf[strlen(linebuf)-1]=='\n')
linebuf[strlen(linebuf)-1]='\0';
if(linebuf[0]=='#')
continue;
user = cvs_strtok(linebuf,":");
if(!user || !*user)
continue;
passnode = new_passwd_entry();
passnode->username=xstrdup(user);
passnode->password=xstrdup(cvs_strtok(NULL,":"));
passnode->real_username=xstrdup(cvs_strtok(NULL,":"));
xfree (linebuf);
linebuf = NULL;
}
if (ferror (fp))
{
error (1, errno, "cannot read %s", filename);
}
if (fclose (fp) < 0)
error (0, errno, "cannot close %s", filename);
}
return 0;
}
int write_passwd_list()
{
FILE *fp;
char *filename;
filename = (char*)xmalloc(strlen(current_parsed_root->directory)
+ strlen("/CVSROOT/passwd")
+ 1);
strcpy (filename, current_parsed_root->directory);
strcat (filename, "/CVSROOT/passwd");
fp = CVS_FOPEN (filename, "w");
if (fp == NULL)
{
error (0, errno, "cannot open %s for writing", filename);
}
else
{
int u;
for(u=0; u<passwd_list_size; u++)
write_passwd_entry(fp, u);
if (fclose (fp) < 0)
error (0, errno, "cannot close %s", filename);
}
xfree(filename);
return 0;
}
static const char *const passwd_usage[] =
{
"Usage: %s %s [-a] [-x] [-X] [-r real_user] [-R] [-D domain] [username]\n",
"\t-a\tAdd user\n",
"\t-x\tDisable user\n",
"\t-X\tDelete user\n",
"\t-r\tAlias username to real system user\n",
"\t-R\tRemove alias to real system user\n",
"\t-D\tUse domain password\n",
NULL
};
int passwd (int argc, char **argv)
{
int c;
int err = 0;
char typed_password[128]={0}, typed_password2[128]={0};
const char *username, *user;
passwd_entry *passnode;
char *real_user = NULL;
char *password_domain = NULL;
int adduser=0,deluser=0,disableuser=0,realuser=0,remove_realuser=0,use_domain=0;
int arg_specified = 0;
if (argc == -1)
usage (passwd_usage);
optind = 0;
while ((c = getopt (argc, argv, "+axXr:RD:")) != -1)
{
switch (c)
{
case 'a':
if(arg_specified)
usage (passwd_usage);
arg_specified = 1;
adduser = 1;
break;
case 'x':
if(arg_specified)
usage (passwd_usage);
arg_specified = 1;
disableuser = 1;
break;
case 'X':
if(arg_specified)
usage (passwd_usage);
arg_specified = 1;
deluser = 1;
break;
case 'r':
realuser = 1;
real_user = xstrdup(optarg);
break;
case 'R':
remove_realuser = 1;
break;
case 'D':
use_domain = 1;
password_domain = xstrdup(optarg);
break;
case '?':
default:
usage (passwd_usage);
break;
}
}
argc -= optind;
argv += optind;
if(!argc)
user = NULL;
else
user=argv[0];
if (current_parsed_root->isremote)
{
if (argc > 1)
usage (passwd_usage);
if (!supported_request ("passwd"))
error (1, 0, "server does not support passwd");
if(!user && adduser)
{
error(1,0,"You cannot add yourself");
}
if(!user && deluser)
{
error(1,0,"You cannot delete yourself");
}
if(user || current_parsed_root->username || current_parsed_root->hostname)
{
printf ("%s %s@%s\n",
(adduser) ? "Adding user" : (deluser) ? "Deleting user" : "Changing repository password for",
user?user:current_parsed_root->username?current_parsed_root->username:getcaller(),current_parsed_root->hostname);
}
else
{
printf ("Changing repository password for %s\n",getcaller());
}
fflush (stdout);
if(!use_domain && !deluser && !disableuser)
{
CProtocolLibrary lib;
if(!lib.PromptForPassword("New Password: ",typed_password,sizeof(typed_password)))
error(1,0,"Aborted");
if(!lib.PromptForPassword("Verify Password: ",typed_password2,sizeof(typed_password2)))
error(1,0,"Aborted");
if (strcmp(typed_password, typed_password2) != 0)
{
memset (typed_password, 0, strlen (typed_password));
memset (typed_password2, 0, strlen (typed_password2));
error (1, 0, "Passwords do not match, try again");
}
memset (typed_password2, 0, strlen (typed_password2));
if(strlen(typed_password))
crypt_password(typed_password);
}
if (adduser)
send_arg ("-a");
if (disableuser)
send_arg ("-x");
if (deluser)
send_arg ("-X");
if (realuser)
{
send_arg ("-r");
send_arg (real_user);
}
if (remove_realuser)
send_arg ("-R");
if(use_domain)
{
send_arg ("-D");
send_arg (password_domain);
}
if (argc == 1)
send_arg(user);
else
send_arg("*");
if(typed_password)
{
send_arg (typed_password); /* Send the new password */
memset (typed_password, 0, strlen (typed_password));
}
send_to_server ("passwd\n", 0);
return get_responses_and_close ();
}
if(!server_active)
{
if(argc!=0 && argc!=1)
usage (passwd_usage);
if(!user && adduser)
{
error(1,0,"You cannot add yourself");
}
if(!user && deluser)
{
error(1,0,"You cannot delete yourself");
}
if(user || current_parsed_root->username)
{
printf ("%s %s\n",
(adduser) ? "Adding user" : (deluser) ? "Deleting user" : "Changing password for",
user?user:current_parsed_root->username);
}
else
{
printf ("Changing repository password for %s\n",getcaller());
}
fflush (stdout);
if (argc == 0)
username = CVS_Username;
else
{
username = user;
}
if(!use_domain && !deluser && !disableuser)
{
CProtocolLibrary lib;
if(!lib.PromptForPassword("New Password: ",typed_password,sizeof(typed_password)))
error(1,0,"Aborted");
if(!lib.PromptForPassword("Verify Password: ",typed_password2,sizeof(typed_password2)))
error(1,0,"Aborted");
if (strcmp(typed_password, typed_password2) != 0)
{
memset (typed_password, 0, strlen (typed_password));
memset (typed_password2, 0, strlen (typed_password2));
error (1, 0, "Passwords do not match, try again");
}
memset (typed_password2, 0, strlen (typed_password2));
if(strlen(typed_password))
crypt_password(typed_password);
}
}
#ifdef SERVER_SUPPORT
if(server_active)
{
if ((argc != 1) && (argc != 2))
usage (passwd_usage);
if(!strcmp(user,"*"))
username = CVS_Username;
else
username = user;
if(argc==2)
strncpy(typed_password,argv[1],sizeof(typed_password));
}
#endif
if (typed_password[0] &&
(strcmp(username, CVS_Username) != 0) &&
(! verify_admin ()))
error (1, 0, "Only administrators can add or change another's password");
read_passwd_list();
passnode = find_passwd_entry(username);
if (passnode == NULL)
{
if (!adduser)
error (1, 0, "Could not find %s in password file", username);
if (! verify_admin())
{
error (1, 0, "Only administrators can add users" );
}
passnode = new_passwd_entry();
passnode->username=xstrdup(username);
passnode->password=xstrdup(typed_password);
passnode->real_username=NULL;
}
if(deluser)
{
if (! verify_admin())
{
error (1, 0, "Only administrators can delete users" );
}
xfree(passnode->username);
passnode->username = NULL;
}
else if(disableuser)
{
if (! verify_admin())
{
error (1, 0, "Only administrators can disable users" );
}
xfree(passnode->password);
passnode->password=xstrdup("#DISABLED#");
}
else
{
xfree(passnode->password);
#ifdef _WIN32 /* Unix servers can't make any sense of this */
if(use_domain)
{
passnode->password = (char*)xmalloc(strlen(password_domain)+2);
strcpy(passnode->password,"!");
strcat(passnode->password,password_domain);
}
else
#endif
passnode->password = xstrdup(typed_password);
if(realuser)
{
if(!getpwnam(real_user))
error(1, 0, "User '%s' is not a real user on the system.",real_user);
xfree(passnode->real_username);
passnode->real_username = xstrdup(real_user);
}
else if (remove_realuser)
{
xfree(passnode->real_username);
passnode->real_username=NULL;
}
if(!runas_user && ((passnode->real_username && !getpwnam(passnode->real_username)) || (!passnode->real_username && passnode->username && !getpwnam(passnode->username))))
{
error(0,0,"*WARNING* CVS user '%s' will not be able to log in until they are aliased to a valid system user.",username);
}
}
write_passwd_list();
free_passwd_list();
xfree(real_user);
xfree(password_domain);
return (err);
}
syntax highlighted by Code2HTML, v. 0.9.1