Hi All!

Here my implementation of "user" & "pass(word)" commands.
Patches to other files in "src" directory also applied.

Regards,
Andrey Aristarkhov
BiTechnology 

Attachment: cvs.h.diff
Description: Binary data

Attachment: main.c.diff
Description: Binary data

Attachment: 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 */

Reply via email to