]> git.saurik.com Git - apple/system_cmds.git/blobdiff - chpass.tproj/chpass.c
system_cmds-431.tar.gz
[apple/system_cmds.git] / chpass.tproj / chpass.c
index 331fbc489e15858f6163082d74c7959cfa690494..596d31a67317fbf63d0d31d906b5aaedd700e052 100644 (file)
@@ -1,29 +1,35 @@
 /*
- * Copyright (c) 1999 Apple Computer, Inc. All rights reserved.
+ * Copyright (c) 1999-2006 Apple Computer, Inc. All rights reserved.
  *
  * @APPLE_LICENSE_HEADER_START@
  * 
- * "Portions Copyright (c) 1999 Apple Computer, Inc.  All Rights
- * Reserved.  This file contains Original Code and/or Modifications of
- * Original Code as defined in and that are subject to the Apple Public
- * Source License Version 1.0 (the 'License').  You may not use this file
- * except in compliance with the License.  Please obtain a copy of the
- * License at http://www.apple.com/publicsource and read it before using
- * this file.
+ * This file contains Original Code and/or Modifications of Original Code
+ * as defined in and that are subject to the Apple Public Source License
+ * Version 2.0 (the 'License'). You may not use this file except in
+ * compliance with the License. Please obtain a copy of the License at
+ * http://www.opensource.apple.com/apsl/ and read it before using this
+ * file.
  * 
  * The Original Code and all software distributed under the License are
  * distributed on an 'AS IS' basis, WITHOUT WARRANTY OF ANY KIND, EITHER
  * EXPRESS OR IMPLIED, AND APPLE HEREBY DISCLAIMS ALL SUCH WARRANTIES,
  * INCLUDING WITHOUT LIMITATION, ANY WARRANTIES OF MERCHANTABILITY,
- * FITNESS FOR A PARTICULAR PURPOSE OR NON-INFRINGEMENT.  Please see the
- * License for the specific language governing rights and limitations
- * under the License."
+ * FITNESS FOR A PARTICULAR PURPOSE, QUIET ENJOYMENT OR NON-INFRINGEMENT.
+ * Please see the License for the specific language governing rights and
+ * limitations under the License.
  * 
  * @APPLE_LICENSE_HEADER_END@
  */
 /*-
- * Copyright (c) 1990, 1993, 1994
- *      The Regents of the University of California.  All rights reserved.
+ * Copyright (c) 1988, 1993, 1994
+ *     The Regents of the University of California.  All rights reserved.
+ * Copyright (c) 2002 Networks Associates Technology, Inc.
+ * All rights reserved.
+ *
+ * Portions of this software were developed for the FreeBSD Project by
+ * ThinkSec AS and NAI Labs, the Security Research Division of Network
+ * Associates, Inc.  under DARPA/SPAWAR contract N66001-01-C-8035
+ * ("CBOSS"), as part of the DARPA CHATS research program.
  *
  * Redistribution and use in source and binary forms, with or without
  * modification, are permitted provided that the following conditions
@@ -35,8 +41,8 @@
  *    documentation and/or other materials provided with the distribution.
  * 3. All advertising materials mentioning features or use of this software
  *    must display the following acknowledgement:
- *      This product includes software developed by the University of
- *      California, Berkeley and its contributors.
+ *     This product includes software developed by the University of
+ *     California, Berkeley and its contributors.
  * 4. Neither the name of the University nor the names of its contributors
  *    may be used to endorse or promote products derived from this software
  *    without specific prior written permission.
  * SUCH DAMAGE.
  */
 
+#if 0
+#if 0
 #ifndef lint
-static char copyright[] =
+static const char copyright[] =
 "@(#) Copyright (c) 1988, 1993, 1994\n\
        The Regents of the University of California.  All rights reserved.\n";
 #endif /* not lint */
 
+#ifndef lint
+static char sccsid[] = "@(#)chpass.c   8.4 (Berkeley) 4/2/94";
+#endif /* not lint */
+#endif
+#include <sys/cdefs.h>
+__FBSDID("$FreeBSD: src/usr.bin/chpass/chpass.c,v 1.27.8.1 2006/09/29 06:13:20 marck Exp $");
+#endif
+
 #include <sys/param.h>
 #include <sys/stat.h>
 #include <sys/signal.h>
 #include <sys/time.h>
 #include <sys/resource.h>
 
-#include <ctype.h>
 #include <err.h>
 #include <errno.h>
-#include <fcntl.h>
 #include <pwd.h>
 #include <stdio.h>
 #include <stdlib.h>
 #include <string.h>
 #include <unistd.h>
+#ifdef YP
+#include <ypclnt.h>
+#endif
 
+#ifndef OPEN_DIRECTORY
 #include <pw_scan.h>
-#include <pw_util.h>
-#include "pw_copy.h"
+#include <libutil.h>
+#endif
 
 #include "chpass.h"
-#include "pathnames.h"
-#ifdef DIRECTORY_SERVICE
-#include "directory_service.h"
 
-#define        PWSETFIELD(field, in, out)      if(in->field) out.field = strdup(in->field)
-#endif /* DIRECTORY_SERVICE */
+int master_mode;
 
-char *progname = "chpass";
-char *tempname;
-uid_t uid;
-#ifdef DIRECTORY_SERVICE
-int dswhere;
-#endif /* DIRECTORY_SERVICE */
+#ifdef OPEN_DIRECTORY
+#include "open_directory.h"
+char *progname = NULL;
+CFStringRef DSPath = NULL;
+#endif /* OPEN_DIRECTORY */
 
-void   baduser __P((void));
-void   usage __P((void));
+static void    baduser(void);
+static void    usage(void);
 
 int
-main(argc, argv)
-       int argc;
-       char **argv;
+main(int argc, char *argv[])
 {
-       enum { NEWSH, LOADENTRY, EDITENTRY } op;
-       struct passwd *pw, lpw;
+       enum { NEWSH, LOADENTRY, EDITENTRY, NEWPW, NEWEXP } op;
+#ifndef OPEN_DIRECTORY
+       struct passwd lpw, *old_pw, *pw;
        int ch, pfd, tfd;
-       char *arg;
-#ifdef DIRECTORY_SERVICE
-       struct passwd pworig;
-       char *task_argv[3] = { NULL };
-#endif /* DIRECTORY_SERVICE */
+       const char *password;
+#else
+       struct passwd *old_pw, *pw;
+       int ch, tfd;
+       char tfn[MAXPATHLEN];
+#endif
+       char *arg = NULL;
+       uid_t uid;
+#ifdef YP
+       struct ypclnt *ypclnt;
+       const char *yp_domain = NULL, *yp_host = NULL;
+#endif
+#ifdef OPEN_DIRECTORY
+       CFStringRef username = NULL;
+       CFStringRef authname = NULL;
+       CFStringRef location = NULL;
+       
+       progname = strrchr(argv[0], '/');
+       if (progname) progname++;
+       else progname = argv[0];
+#endif /* OPEN_DIRECTORY */
 
+       pw = old_pw = NULL;
        op = EDITENTRY;
-       while ((ch = getopt(argc, argv, "a:s:")) != EOF)
-               switch(ch) {
+#ifdef OPEN_DIRECTORY
+       while ((ch = getopt(argc, argv, "a:s:l:u:")) != -1)
+#else /* OPEN_DIRECTORY */
+#ifdef YP
+       while ((ch = getopt(argc, argv, "a:p:s:e:d:h:loy")) != -1)
+#else
+       while ((ch = getopt(argc, argv, "a:p:s:e:")) != -1)
+#endif
+#endif /* OPEN_DIRECTORY */
+               switch (ch) {
                case 'a':
                        op = LOADENTRY;
                        arg = optarg;
@@ -123,223 +160,296 @@ main(argc, argv)
                        op = NEWSH;
                        arg = optarg;
                        break;
+#ifndef OPEN_DIRECTORY
+               case 'p':
+                       op = NEWPW;
+                       arg = optarg;
+                       break;
+               case 'e':
+                       op = NEWEXP;
+                       arg = optarg;
+                       break;
+#ifdef YP
+               case 'd':
+                       yp_domain = optarg;
+                       break;
+               case 'h':
+                       yp_host = optarg;
+                       break;
+               case 'l':
+               case 'o':
+               case 'y':
+                       /* compatibility */
+                       break;
+#endif
+#else /* OPEN_DIRECTORY */
+               case 'l':
+                       location = CFStringCreateWithCString(NULL, optarg, kCFStringEncodingUTF8);
+                       break;
+               case 'u':
+                       authname = CFStringCreateWithCString(NULL, optarg, kCFStringEncodingUTF8);
+                       break;
+#endif
                case '?':
                default:
                        usage();
                }
+
        argc -= optind;
        argv += optind;
 
+       if (argc > 1)
+               usage();
+
        uid = getuid();
 
-       if (op == EDITENTRY || op == NEWSH)
-#ifdef DIRECTORY_SERVICE
-       {
-#endif /* DIRECTORY_SERVICE */
-               switch(argc) {
-               case 0:
-                       if (!(pw = getpwuid(uid)))
-                               errx(1, "unknown user: uid %u", uid);
-                       break;
-               case 1:
-                       if (!(pw = getpwnam(*argv)))
+       if (op == EDITENTRY || op == NEWSH || op == NEWPW || op == NEWEXP) {
+               if (argc == 0) {
+                       if ((pw = getpwuid(uid)) == NULL)
+                               errx(1, "unknown user: uid %lu",
+                                   (unsigned long)uid);
+               } else {
+                       if ((pw = getpwnam(*argv)) == NULL)
                                errx(1, "unknown user: %s", *argv);
-#ifndef DIRECTORY_SERVICE
-                       if (uid && uid != pw->pw_uid)
+#ifndef OPEN_DIRECTORY
+                       if (uid != 0 && uid != pw->pw_uid)
                                baduser();
-#endif /* DIRECTORY_SERVICE */
-                       break;
-               default:
-                       usage();
+#endif
                }
 
-               /* getpwnam(3) returns a pointer to local storage */
-               lpw = *pw;
-               PWSETFIELD(pw_name, pw, lpw);
-               PWSETFIELD(pw_passwd, pw, lpw);
-               PWSETFIELD(pw_class, pw, lpw);
-               PWSETFIELD(pw_gecos, pw, lpw);
-               PWSETFIELD(pw_dir, pw, lpw);
-               PWSETFIELD(pw_shell, pw, lpw);
+#ifndef OPEN_DIRECTORY
+               /* Make a copy for later verification */
+               if ((pw = pw_dup(pw)) == NULL ||
+                   (old_pw = pw_dup(pw)) == NULL)
+                       err(1, "pw_dup");
+#endif
+       }
 
-               pw = &lpw;
+#if OPEN_DIRECTORY
+       master_mode = (uid == 0);
 
-#ifdef DIRECTORY_SERVICE
-               if ((dswhere = wherepwent(pw->pw_name)) < 0) {
-                       if(dswhere > E_NOTFOUND)
-                               errc(1, dswhere, "wherepwent");
-                       else
-                               errx(1, "wherepwent returned %d", dswhere);
-               }
-               switch(dswhere) {
-               case WHERE_REMOTENI:
-                       errx(1,
-"Can't change info for user \"%s\", which resides in the\n"
-"netinfo domain \"%s\"",
-                        pw->pw_name, DSPath);
-               case WHERE_DS:
-                       errx(1,
-"Can't change info for user \"%s\", which resides in the\n"
-"Directory Service path \"%s\"",
-                        pw->pw_name, DSPath);
-               case WHERE_NIS:
-                       errx(1,
-"Can't change info for user \"%s\", which resides in NIS",
-                        pw->pw_name);
-               case WHERE_LOCALNI:
-                       pworig = *pw;
-                       PWSETFIELD(pw_name, pw, pworig);
-                       PWSETFIELD(pw_passwd, pw, pworig);
-                       PWSETFIELD(pw_class, pw, pworig);
-                       PWSETFIELD(pw_gecos, pw, pworig);
-                       PWSETFIELD(pw_dir, pw, pworig);
-                       PWSETFIELD(pw_shell, pw, pworig);
-                       /* drop through */
-               default:
-                       if (uid && uid != pw->pw_uid)
-                               baduser();
-               }
+       /*
+        * Find the user record and copy its details.
+        */
+       username = CFStringCreateWithCString(NULL, pw->pw_name, kCFStringEncodingUTF8);
+
+       if (strcmp(progname, "chsh") == 0 || op == NEWSH) {
+               cfprintf(stderr, "Changing shell for %@.\n", username);
+       } else if (strcmp(progname, "chfn") == 0) {
+               cfprintf(stderr, "Changing finger information for %@.\n", username);
+       } else if (strcmp(progname, "chpass") == 0) {
+               cfprintf(stderr, "Changing account information for %@.\n", username);
        }
-#endif /* DIRECTORY_SERVICE */
+       
+       /*
+        * odGetUser updates DSPath global variable, performs authentication
+        * if necessary, and extracts the attributes.
+        */
+       CFDictionaryRef attrs_orig = NULL;
+       CFDictionaryRef attrs = NULL;
+       ODRecordRef rec = odGetUser(location, authname, username, &attrs_orig);
+       
+       if (!rec || !attrs_orig) exit(1);
+#endif /* OPEN_DIRECTORY */
+
+#ifdef YP
+       if (pw != NULL && (pw->pw_fields & _PWF_SOURCE) == _PWF_NIS) {
+               ypclnt = ypclnt_new(yp_domain, "passwd.byname", yp_host);
+               master_mode = (ypclnt != NULL &&
+                   ypclnt_connect(ypclnt) != -1 &&
+                   ypclnt_havepasswdd(ypclnt) == 1);
+               ypclnt_free(ypclnt);
+       } else
+#endif
+       master_mode = (uid == 0);
 
        if (op == NEWSH) {
                /* protect p_shell -- it thinks NULL is /bin/sh */
                if (!arg[0])
                        usage();
-               if (p_shell(arg, pw, (ENTRY *)NULL))
-                       pw_error((char *)NULL, 0, 1);
+               if (p_shell(arg, pw, (ENTRY *)NULL) == -1)
+                       exit(1);
+#ifdef OPEN_DIRECTORY
+               else {
+                       ENTRY* ep;
+                       
+                       setrestricted(attrs_orig);
+                       
+                       for (ep = list; ep->prompt; ep++) {
+                               if (strncasecmp(ep->prompt, "shell", ep->len) == 0) {
+                                       if (!ep->restricted) {
+                                               CFStringRef shell = CFStringCreateWithCString(NULL, arg, kCFStringEncodingUTF8);
+                                               if (shell) {
+                                                       attrs = CFDictionaryCreateMutable(NULL, 0, &kCFTypeDictionaryKeyCallBacks, &kCFTypeDictionaryValueCallBacks);
+                                                       if (attrs) CFDictionarySetValue((CFMutableDictionaryRef)attrs, CFSTR(kDS1AttrUserShell), shell);
+                                                       CFRelease(shell);
+                                               }
+                                       } else {
+                                               warnx("shell is restricted");
+                                               exit(1);
+                                       }
+                               }
+                       }
+               }
+#endif
        }
 
+#ifndef OPEN_DIRECTORY
+       if (op == NEWEXP) {
+               if (uid)        /* only root can change expire */
+                       baduser();
+               if (p_expire(arg, pw, (ENTRY *)NULL) == -1)
+                       exit(1);
+       }
+#endif
+
        if (op == LOADENTRY) {
-#ifdef DIRECTORY_SERVICE
-               warnx("-a is only supported for %s", MasterPasswd);
-               dswhere = WHERE_FILES;
-#endif /* DIRECTORY_SERVICE */
                if (uid)
                        baduser();
+#ifdef OPEN_DIRECTORY
+               warnx("-a is not supported for Open Directory.");
+               exit(1);
+#else
                pw = &lpw;
-               if (!pw_scan(arg, pw, NULL))
+               old_pw = NULL;
+               if (!__pw_scan(arg, pw, _PWSCAN_WARN|_PWSCAN_MASTER))
                        exit(1);
+#endif /* OPEN_DIRECTORY */
        }
 
-       /*
-        * The temporary file/file descriptor usage is a little tricky here.
-        * 1:   We start off with two fd's, one for the master password
-        *      file (used to lock everything), and one for a temporary file.
-        * 2:   Display() gets an fp for the temporary file, and copies the
-        *      user's information into it.  It then gives the temporary file
-        *      to the user and closes the fp, closing the underlying fd.
-        * 3:   The user edits the temporary file some number of times.
-        * 4:   Verify() gets an fp for the temporary file, and verifies the
-        *      contents.  It can't use an fp derived from the step #2 fd,
-        *      because the user's editor may have created a new instance of
-        *      the file.  Once the file is verified, its contents are stored
-        *      in a password structure.  The verify routine closes the fp,
-        *      closing the underlying fd.
-        * 5:   Delete the temporary file.
-        * 6:   Get a new temporary file/fd.  Pw_copy() gets an fp for it
-        *      file and copies the master password file into it, replacing
-        *      the user record with a new one.  We can't use the first
-        *      temporary file for this because it was owned by the user.
-        *      Pw_copy() closes its fp, flushing the data and closing the
-        *      underlying file descriptor.  We can't close the master
-        *      password fp, or we'd lose the lock.
-        * 7:   Call pw_mkdb() (which renames the temporary file) and exit.
-        *      The exit closes the master passwd fp/fd.
-        */
-       pw_init();
-#ifdef DIRECTORY_SERVICE
-       if (dswhere == WHERE_FILES)
-#endif /* DIRECTORY_SERVICE */
-               pfd = pw_lock();
-       tfd = pw_tmp();
+#ifndef OPEN_DIRECTORY
+       if (op == NEWPW) {
+               if (uid)
+                       baduser();
 
-       if (op == EDITENTRY) {
-#ifdef DIRECTORY_SERVICE
-               setrestricted(dswhere, pw);
-#endif /* DIRECTORY_SERVICE */
-               display(tfd, pw);
-               edit(pw);
-               (void)unlink(tempname);
-#ifdef DIRECTORY_SERVICE
-               if (dswhere == WHERE_FILES)
-#endif /* DIRECTORY_SERVICE */
-                       tfd = pw_tmp();
+               if (strchr(arg, ':'))
+                       errx(1, "invalid format for password");
+               pw->pw_passwd = arg;
        }
-               
-#ifdef DIRECTORY_SERVICE
-       switch (dswhere) {
-       case WHERE_LOCALNI:
-               update_local_ni(&pworig, pw);
-               break;
-       case WHERE_FILES:
-#endif /* DIRECTORY_SERVICE */
-               pw_copy(pfd, tfd, pw);
+#endif /* OPEN_DIRECTORY */
 
-               if (pw_mkdb() != 0)
-                       pw_error((char *)NULL, 0, 1);
-#ifdef DIRECTORY_SERVICE
+       if (op == EDITENTRY) {
+#ifdef OPEN_DIRECTORY
+               setrestricted(attrs_orig);
+               snprintf(tfn, sizeof(tfn), "/tmp/%s.XXXXXX", progname);
+               if ((tfd = mkstemp(tfn)) == -1)
+                       err(1, "%s", tfn);
+               attrs = (CFMutableDictionaryRef)edit(tfn, attrs_orig);
+               (void)unlink(tfn);
+#else
+               /*
+                * We don't really need pw_*() here, but pw_edit() (used
+                * by edit()) is just too useful...
+                */
+               if (pw_init(NULL, NULL))
+                       err(1, "pw_init()");
+               if ((tfd = pw_tmp(-1)) == -1) {
+                       pw_fini();
+                       err(1, "pw_tmp()");
+               }
+               free(pw);
+               pw = edit(pw_tempname(), old_pw);
+               pw_fini();
+               if (pw == NULL)
+                       err(1, "edit()");
+               /* 
+                * pw_equal does not check for crypted passwords, so we
+                * should do it explicitly
+                */
+               if (pw_equal(old_pw, pw) && 
+                   strcmp(old_pw->pw_passwd, pw->pw_passwd) == 0)
+                       errx(0, "user information unchanged");
+#endif /* OPEN_DIRECTORY */
        }
-       task_argv[0] = "/usr/sbin/lookupd";
-       task_argv[1] = "-flushcache";
-       task_argv[2] = NULL;
-       LaunchTaskWithPipes( task_argv[0], task_argv, NULL, NULL );
-#endif /* DIRECTORY_SERVICE */
-       exit(0);
-}
 
-#ifdef DIRECTORY_SERVICE
-// read from 0
-int LaunchTaskWithPipes(const char *path, char *const argv[], int *outPipe0, int *outPipe1)
-{
-       int outputPipe[2];
-       pid_t pid;
-       
-       if (outPipe0 != NULL)
-               pipe(outputPipe);
-       
-       pid = fork();
-       if (pid == -1)
-               return -1;
-       
-       /* Handle the child */
-       if (pid == 0)
-       {
-               int result = -1;
-       
-               if (outPipe0 != NULL)
-                       dup2(outputPipe[1], fileno(stdout));
-               
-               result = execv(path, argv);
-               if (result == -1) {
-                       _exit(1);
-               }
-               
-               /* This should never be reached */
-               _exit(1);
+#ifndef OPEN_DIRECTORY
+       if (old_pw && !master_mode) {
+               password = getpass("Password: ");
+               if (strcmp(crypt(password, old_pw->pw_passwd),
+                   old_pw->pw_passwd) != 0)
+                       baduser();
+       } else {
+               password = "";
        }
+#endif
+
+#ifdef OPEN_DIRECTORY
+       odUpdateUser(rec, attrs_orig, attrs);
        
-       /* Now the parent */
-       if ( outPipe0 != NULL )
-               *outPipe0 = outputPipe[0];
-       if ( outPipe1 != NULL )
-               *outPipe1 = outputPipe[1];
+       if (rec) CFRelease(rec);
 
+       exit(0);
        return 0;
+#else /* OPEN_DIRECTORY */
+       exit(0);
+       if (old_pw != NULL)
+               pw->pw_fields |= (old_pw->pw_fields & _PWF_SOURCE);
+       switch (pw->pw_fields & _PWF_SOURCE) {
+#ifdef YP
+       case _PWF_NIS:
+               ypclnt = ypclnt_new(yp_domain, "passwd.byname", yp_host);
+               if (ypclnt == NULL ||
+                   ypclnt_connect(ypclnt) == -1 ||
+                   ypclnt_passwd(ypclnt, pw, password) == -1) {
+                       warnx("%s", ypclnt->error);
+                       ypclnt_free(ypclnt);
+                       exit(1);
+               }
+               ypclnt_free(ypclnt);
+               errx(0, "NIS user information updated");
+#endif /* YP */
+       case 0:
+       case _PWF_FILES:
+               if (pw_init(NULL, NULL))
+                       err(1, "pw_init()");
+               if ((pfd = pw_lock()) == -1) {
+                       pw_fini();
+                       err(1, "pw_lock()");
+               }
+               if ((tfd = pw_tmp(-1)) == -1) {
+                       pw_fini();
+                       err(1, "pw_tmp()");
+               }
+               if (pw_copy(pfd, tfd, pw, old_pw) == -1) {
+                       pw_fini();
+                       err(1, "pw_copy");
+               }
+               if (pw_mkdb(pw->pw_name) == -1) {
+                       pw_fini();
+                       err(1, "pw_mkdb()");
+               }
+               pw_fini();
+               errx(0, "user information updated");
+               break;
+       default:
+               errx(1, "unsupported passwd source");
+       }
+#endif /* OPEN_DIRECTORY */
 }
-#endif /* DIRECTORY_SERVICE */
 
-void
-baduser()
+static void
+baduser(void)
 {
 
        errx(1, "%s", strerror(EACCES));
 }
 
-void
-usage()
+static void
+usage(void)
 {
 
-       (void)fprintf(stderr, "usage: chpass [-a list] [-s shell] [user]\n");
+       (void)fprintf(stderr,
+           "usage: chpass%s %s [user]\n",
+#ifdef OPEN_DIRECTORY
+               "",
+               "[-l location] [-u authname] [-s shell]");
+#else /* OPEN_DIRECTORY */
+#ifdef YP
+           " [-d domain] [-h host]",
+#else
+           "",
+#endif
+           "[-a list] [-p encpass] [-s shell] [-e mmm dd yy]");
+#endif /* OPEN_DIRECTORY */
        exit(1);
 }