[Date Prev][Date Next]   [Thread Prev][Thread Next]   [Thread Index] [Date Index] [Author Index]

pam_unix_passwd and NIS


I was all set to fix pam_unix_passwd to understand NIS today.
Unfortunately the source scared me off.  I've fixed this in util-linux
passwd, it works very well. I'm enclosing the setpwnam routine used
there.  Unfortunately it's under LGPL, y'all seem to favour the
berkely license?  If the LGPL is ok to you there is no problem having
LGPLed source in a separate file, the LGPL won't infect the rest of the

- Do _not_ use getpw*. All these, when faced with a NIS password file
  with +* entries, will fetch password entries from the NIS passwd
  map.  The effect is to make a local copy of the NIS passwd map
  in /etc/passwd with no traces of the +* entries.  That is obviously
- Do _not_ use fgetpw* either.  Same problem.
- _Do_ read /etc/passwd directly.  There is nothing evil about this in
  the passwd program.  We are after all implementing a /etc/passwd
  management program, if we don't know the format nothing does.

Here is setpwnam.c from util-linux.  The missing parts (setpwnam.h
and pathnames.h, islocal.c for 'is this a local password' check, and
passwd.c for usage example) are at http://www.math.uio.no/~janl/pam/,
I'll mail it to anyone that wants it, but I think setpwnam.c is quite
enough for one mail.


 *  setpwnam.c --
 *  edit an entry in a password database.
 *  (c) 1994 Salvatore Valente <svalente@mit.edu>
 *  This file is free software; you can redistribute it and/or
 *  modify it under the terms of the GNU Library General Public License as
 *  published by the Free Software Foundation; either version 2 of the
 *  License, or (at your option) any later version.
 *  Edited 11/10/96 (DD/MM/YY ;-) by Nicolai Langfeldt (janl@math.uio.no)
 *  to read /etc/passwd directly so that passwd, chsh and chfn can work
 *  on machines that run NIS (né YP).  Changes will not be made to
 *  usernames starting with +.
 *  This file is distributed with no warranty.
 *  Usage:
 *  1) get a struct passwd * from getpwnam().
 *     You should assume a struct passwd has an infinite number of fields,
 *     so you should not try to create one from scratch.
 *  2) edit the fields you want to edit.
 *  3) call setpwnam() with the edited struct passwd.
 *  A _normal user_ program should never directly manipulate
 *  /etc/passwd but use getpwnam() and (family, as well as)
 *  setpwnam().
 *  But, setpwnam was made to _edit_ the password file.  For use by
 *  chfn, chsh and passwd.  _I_ _HAVE_ to read and write /etc/passwd
 *  directly.  Let those who say nay be forever silent and think about
 *  how getpwnam (and family) works on a machine running YP.
 *  Thanks to "two guys named Ian".
 *   $Author: janl $
 *   $Revision: 1.12 $
 *   $Date: 1996/11/11 00:35:37 $

#undef DEBUG

/*  because I use getpwent(), putpwent(), etc... */
#define _SVID_SOURCE

#include <sys/types.h>
#include <stdio.h>
#include <string.h>
#include <stdlib.h>
#include <unistd.h>
#include <fcntl.h>
#include <pwd.h>
#include <errno.h>
#include <signal.h>
#include <sys/resource.h>
#include <sys/stat.h>
#include <paths.h>

#include "setpwnam.h"

static void pw_init(void);

int setpwnam (struct passwd *pwd)

 *  setpwnam () --
 *	takes a struct passwd in which every field is filled in and valid.
 *	If the given username exists in the passwd file, the entry is
 *	replaced with the given entry.

  FILE *fp, *pwf;
  int x, save_errno, fd, ret;
  struct passwd *entry;
  boolean found;
  int oldumask;
  int namelen;
  int buflen=256;
  int contlen;
  char *linebuf=malloc(buflen);

  oldumask = umask(0);   /* Create with exact permissions */


  /* sanity check */
  for (x = 0; x < 3; x++) {
    if (x > 0) sleep (1);
    fd = open (PTMPTMP_FILE, O_WRONLY|O_CREAT, 0644);
    if(fd == -1) {
      return (-1);
    ret = link(PTMPTMP_FILE, PTMP_FILE);
    if(ret == -1)

  if (ret == -1) return (-1);

  /* ptmp should be owned by root.root or root.wheel */
  if (chown (PTMP_FILE, (uid_t) 0, (gid_t) 0) < 0)
    perror ("chown");

  /* open ptmp for writing and passwd for reading */
  fp = fdopen (fd, "w");
  if (! fp) goto fail;

  pwf = fopen (PASSWD_FILE, "r");
  if (! pwf) goto fail;


  /* parse the passwd file */
  found = false;
  /* Do you wonder why I don't use getpwent? Read comments at top of file */
  while (fgets(linebuf,buflen,pwf)!=NULL) {
    while (linebuf[contlen-1]!='\n' && !feof(pwf)) {
      /* Extend input buffer if it failed getting the whole line */

      /* So now we double the buffer size */
      buflen *= 2;

      if (linebuf==NULL) {
	fprintf(stderr,"realloc failed\n");
	goto fail;
      /* And fill the rest of the buffer */
      if (fgets(&linebuf[contlen],buflen/2,pwf)==NULL) break;
      /* That was a lot of work for nothing.  Gimme perl! */

    /* Is this the username we were sent to change? */
    if (!found && linebuf[namelen] == ':' &&
	!strncmp(linebuf, pwd->pw_name, namelen)) {
      /* Yes! So go forth in the name of the Lord and change it! */
      if (putpwent(pwd,fp) < 0) goto fail;
    /* Nothing in particular happened, copy input to output */

  if (fclose (fp) < 0) goto fail;
  close (fd);
  fclose (pwf); /* I don't think I want to know if this failed */

  if (! found) {
    errno = ENOENT; /* give me something better */
    goto fail;

  /* we don't care if we can't remove the backup file */
  unlink (PASSWD_FILE".OLD");
  /* we don't care if we can't create the backup file */
  /* we DO care if we can't rename to the passwd file */
  if (rename (PTMP_FILE, PASSWD_FILE) < 0)
    goto fail;
  /* finally:  success */
  return 0;

  save_errno = errno;
  if (fp!=NULL) fclose (fp);
  if (pwf!=NULL) fclose(pwf);
  if (fd >= 0) close (fd);
  if (linebuf!=NULL) free(linebuf);
  unlink (PTMP_FILE);
  errno = save_errno;
  return (-1);

static void pw_init()

     /* Set up the limits so that we're not foiled */

  struct rlimit rlim;

  /* Unlimited resource limits. */
  rlim.rlim_cur = rlim.rlim_max = RLIM_INFINITY;
  (void)setrlimit(RLIMIT_CPU, &rlim);
  (void)setrlimit(RLIMIT_FSIZE, &rlim);
  (void)setrlimit(RLIMIT_STACK, &rlim);
  (void)setrlimit(RLIMIT_DATA, &rlim);
  (void)setrlimit(RLIMIT_RSS, &rlim);

#ifndef DEBUG
  /* Don't drop core (not really necessary, but GP's). */
  rlim.rlim_cur = rlim.rlim_max = 0;
  (void)setrlimit(RLIMIT_CORE, &rlim);

  /* Turn off signals. */
  (void)signal(SIGALRM, SIG_IGN);
  (void)signal(SIGHUP, SIG_IGN);
  (void)signal(SIGINT, SIG_IGN);
  (void)signal(SIGPIPE, SIG_IGN);
  (void)signal(SIGQUIT, SIG_IGN);
  (void)signal(SIGTERM, SIG_IGN);
  (void)signal(SIGTSTP, SIG_IGN);
  (void)signal(SIGTTOU, SIG_IGN);

  /* Create with exact permissions. */

[Date Prev][Date Next]   [Thread Prev][Thread Next]   [Thread Index] [Date Index] [Author Index] []