How to change both the Unix and SMB passwords for a user?

Extracted from comp.protocols.smb
Tip provided by Dean Pentcheff
> But how can I sync the passwords
> the other way? I mean, when a user changes his password on the
> linux-server, how can I sync this with samba?

Code appended.  Have users use this instead of "passwd" (you may
prefer to move passwd to something else and install this instead --
note that you'll need to change the constant in this program to point
to the "real" Unix passwd program that you just renamed).

===== code begins (delete this line) =====
/*
 * cpasswords.c 
 * Copyright N. Dean Pentcheff  1998
 * University of South Carolina
 * dean2@mail.biol.sc.edu
 * This program may be redistributed either under the terms of the GNU Copyleft
 *   or the Perl Artistic License (http://www.perl.com).
 *
 * Change both the Unix and SMB passwords for a user.
 *
 * To work, must be installed SUID-root.
 * Uses the current user's username, except that if the program is called
 *   by the root user, a username can be given as an argument.
 * If called interactively (from a tty), is slightly verbose, and uses
 *   the standard getpass() routine to query for a password twice.
 * If called noninteractively, expects the password (once) on stdin.
 *
 * Customize the locations of the standard Unix and SMB password programs
 *   in the "#defines" near the top (do NOT be tempted to add code to make
 *   these changeable from command-line arguments: these programs will
 *   be run as root!).  If your paths are obscenely long, examine the
 *   size of STRLEN to make sure it will accomodate them.
 * The only check on password quality is existence (len > 0) and for
 *   non-root callers a minimum length (MINPWLEN).  This can be enhanced.
 * The sleep()s in the actual pwd-changing routines appeared to be necessary
 *   in some early tests I did with the PAM-passwd program on Linux.  I'm
 *   not convinced they're always necessary.  Delays in a pwd-changing
 *   program aren't a bad idea anyway, so I've left them in.
 */
#include <fcntl.h>
#include <sys/ioctl.h>
#include <pwd.h>
#include <errno.h>
#include <stdio.h>
#include <unistd.h>
#include <string.h>

#define  PASSWD    "/usr/bin/passwd"
#define  SMBPASSWD "/usr/local/bin/smbpasswd"
#define  PROMPT1   "Type a new password: "
#define  PROMPT2   "Type the same password again: "
#define  MINPWLEN  6
#define  STRLEN    1024

int change(char *program, char *user, char *pwd, FILE *mystderr);

int main (
          int  argc, 
          char **argv)
{
  int    fd;
  struct passwd *pwentry;
  char   name[STRLEN];
  char   newpw[STRLEN];
  int    reallyroot = 0;
  char   *cp;
  FILE   *mystderr;


  /* do we have the appropriate permissions? */
  if (geteuid() != 0) {
    fprintf(stderr, "This program cannot run unless it is SUID-root, "
            "exiting...\n");
    exit(1);
  }
  if (getuid() == 0)
    reallyroot = 1;


  /* get the appropriate username */
  if (argc > 1) {
    if (reallyroot) {
      /* if root, we can specify a username */
      strncpy(name, *++argv, STRLEN);
    } else {
      fprintf(stderr, "Only the root user can specify a name, exiting...\n");
      exit(1);
    }
  } else {
    /* pick up the current user's username */
    if ((pwentry = getpwuid(getuid())) == NULL) {
      fprintf(stderr, "Failed getting name entry for UID=%d, exiting...\n", 
              getuid());
      exit(1);
    }
    strncpy(name, pwentry->pw_name, STRLEN);
  }


  /* get a password and clean any cr/lf stuff */
  if (isatty(0)) { 
    /* interactive, so use a no-echo prompt twice */
    fprintf(stderr, "Changing password for user '%s'\n", name);
    cp = getpass(PROMPT1);
    strncpy(newpw, cp, STRLEN);
    cp = getpass(PROMPT2);
    if (strcmp(newpw, cp)) {
      fprintf(stderr, "The two versions don't match, exiting...\n");
      exit(1);
    }
  } else { 
    /* noninteractive, so just get it from stdin */
    if (read(0, newpw, STRLEN) <= 0) {
      fprintf(stderr, "Failed to read a new password, exiting...\n");
      exit(1);
    }
  }
  for (cp=newpw;  *cp!='\n' && *cp!='\r' && cp-newpw= 0) {
    if (ioctl(fd, TIOCNOTTY) < 0) {
      fprintf(mystderr, "Failed to detach from /dev/tty: %s, exiting...\n", 
              strerror(errno));
      exit(1);
    }
    close(fd);
  }

  
  /* shuffle UIDs for permissions - we know we are running SUID-root */
  if (setuid(geteuid()) != 0) {
    fprintf(stderr, "Failed to properly set UID, exiting...\n");
    exit(1);
  }


  /* change the Unix password */
  if (isatty(0))
    fprintf(mystderr, "Changing Unix password...\n");
  if ( ! change(PASSWD, name, newpw, mystderr))
    exit(1);
  if (isatty(0))
    fprintf(mystderr, "\tSuccessfully changed Unix password.\n");


  /* change the SMB password */
  if (isatty(0))
    fprintf(mystderr, "Changing SMB/Windows password...\n");
  if ( ! change(SMBPASSWD, name, newpw, mystderr))
    exit(1);
  if (isatty(0))
    fprintf(mystderr, "\tSuccessfully changed SMB/Windows password.\n");


  exit(0);
}


int
change(char *program,
       char *user,
       char *pwd,
       FILE *mystderr)
{
  char   cmd[STRLEN];
  FILE   *cmdpipe;
  int    status;

  /* open a pipe to and then feed the password program, slowly */
  strncpy(cmd, program, STRLEN);
  strncat(cmd, " ", STRLEN - 1);
  strncat(cmd, user, STRLEN - strlen(cmd));
  if ((cmdpipe = popen(cmd, "w")) == NULL) {
    fprintf(mystderr, "Failed to open pipe to '%s', exiting...\n", cmd);
    return 0;
  }
  sleep(3);
  fprintf(cmdpipe, "%s\n", pwd); fflush(cmdpipe); sleep(2);
  fprintf(cmdpipe, "%s\n", pwd); fflush(cmdpipe); sleep(2);
  if ((status = pclose(cmdpipe)) != 0) {
    fprintf(mystderr, "Program '%s' returned error code %d, exiting...\n", 
            cmd, status);
    return 0;
  }

  return 1;
}
============ code ends ==============


Follow-up :
| Previous | Next | Index of category | Main Index | Submit |


Appears in section(s) : samba
Tip recorded : 07-12-1998 22:50:02
HTML page last changed : 27-07-1999 20:06:25