]> git.cameronkatri.com Git - pw-darwin.git/blobdiff - chpass/pw_yp.c
Removed extra (non-trailing) arg in a call to warnx().
[pw-darwin.git] / chpass / pw_yp.c
index 6fd87f54707944c485789881075f1502c39a0d9c..777ec54d4633a88251c544a87b244a84d957bdcb 100644 (file)
@@ -20,7 +20,7 @@
  * THIS SOFTWARE IS PROVIDED BY Bill Paul AND CONTRIBUTORS ``AS IS'' AND
  * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
  * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
- * ARE DISCLAIMED.  IN NO EVENT SHALL THE REGENTS OR CONTRIBUTORS BE LIABLE
+ * ARE DISCLAIMED.  IN NO EVENT SHALL Bill Paul OR CONTRIBUTORS BE LIABLE
  * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
  * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS
  * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION)
@@ -35,7 +35,7 @@
  * Center for Telecommunications Research
  * Columbia University, New York City
  *
- *     $Id$
+ *     $Id: pw_yp.c,v 1.11 1997/07/29 15:45:36 wpaul Exp $
  */
 
 #ifdef YP
@@ -45,6 +45,7 @@
 #include <netdb.h>
 #include <time.h>
 #include <sys/types.h>
+#include <sys/stat.h>
 #include <pwd.h>
 #include <errno.h>
 #include <err.h>
 #include <sys/param.h>
 #include <limits.h>
 #include <rpc/rpc.h>
-#include <rpcsvc/yp_prot.h>
+#include <rpcsvc/yp.h>
+struct dom_binding {};
 #include <rpcsvc/ypclnt.h>
 #include <rpcsvc/yppasswd.h>
 #include <pw_util.h>
 #include "pw_yp.h"
+#include "ypxfr_extern.h"
+#include "yppasswd_private.h"
 
 #define PERM_SECURE (S_IRUSR|S_IWUSR)
-HASHINFO openinfo = {
+static HASHINFO openinfo = {
         4096,           /* bsize */
         32,             /* ffactor */
         256,            /* nelem */
@@ -73,75 +77,305 @@ HASHINFO openinfo = {
         0,              /* lorder */
 };
 
+int force_old = 0;
 int _use_yp = 0;
+int suser_override = 0;
+int yp_in_pw_file = 0;
+char *yp_domain = NULL;
+char *yp_server = NULL;
+
+extern char *tempname;
+
+/* Save the local and NIS password information */
+struct passwd local_password;
+struct passwd yp_password;
+
+void copy_yp_pass(p, x, m)
+char *p;
+int x, m;
+{
+       register char *t, *s = p;
+       static char *buf;
+
+       yp_password.pw_fields = 0;
+
+       buf = (char *)realloc(buf, m + 10);
+       bzero(buf, m + 10);
+
+       /* Turn all colons into NULLs */
+       while (strchr(s, ':')) {
+               s = (strchr(s, ':') + 1);
+               *(s - 1)= '\0';
+       }
+
+       t = buf;
+#define EXPAND(e)       e = t; while ((*t++ = *p++));
+        EXPAND(yp_password.pw_name);
+       yp_password.pw_fields |= _PWF_NAME;
+        EXPAND(yp_password.pw_passwd);
+       yp_password.pw_fields |= _PWF_PASSWD;
+       yp_password.pw_uid = atoi(p);
+        p += (strlen(p) + 1);
+       yp_password.pw_fields |= _PWF_UID;
+       yp_password.pw_gid = atoi(p);
+        p += (strlen(p) + 1);
+       yp_password.pw_fields |= _PWF_GID;
+       if (x) {
+               EXPAND(yp_password.pw_class);
+               yp_password.pw_fields |= _PWF_CLASS;
+               yp_password.pw_change = atol(p);
+               p += (strlen(p) + 1);
+               yp_password.pw_fields |= _PWF_CHANGE;
+               yp_password.pw_expire = atol(p);
+               p += (strlen(p) + 1);
+               yp_password.pw_fields |= _PWF_EXPIRE;
+       }
+        EXPAND(yp_password.pw_gecos);
+       yp_password.pw_fields |= _PWF_GECOS;
+        EXPAND(yp_password.pw_dir);
+       yp_password.pw_fields |= _PWF_DIR;
+        EXPAND(yp_password.pw_shell);
+       yp_password.pw_fields |= _PWF_SHELL;
+
+       return;
+}
+
+void copy_local_pass(p,m)
+char *p;
+int m;
+{
+       register char *t;
+       static char *buf;
+
+       buf = (char *)realloc(buf, m + 10);
+       bzero(buf, m + 10);
+
+       t = buf;
+        EXPAND(local_password.pw_name);
+        EXPAND(local_password.pw_passwd);
+        bcopy(p, (char *)&local_password.pw_uid, sizeof(int));
+        p += sizeof(int);
+        bcopy(p, (char *)&local_password.pw_gid, sizeof(int));
+        p += sizeof(int);
+               bcopy(p, (char *)&local_password.pw_change, sizeof(time_t));
+               p += sizeof(time_t);
+               EXPAND(local_password.pw_class);
+        EXPAND(local_password.pw_gecos);
+        EXPAND(local_password.pw_dir);
+        EXPAND(local_password.pw_shell);
+        bcopy(p, (char *)&local_password.pw_expire, sizeof(time_t));
+        p += sizeof(time_t);
+        bcopy(p, (char *)&local_password.pw_fields, sizeof local_password.pw_fields);
+        p += sizeof local_password.pw_fields;
+
+       return;
+}
+
+/*
+ * It is not mandatory that an NIS master server also be a client.
+ * However, if the NIS master is not configured as a client, then the
+ * domain name will not be set and ypbind will not be running, so we
+ * will be unable to use the ypclnt routines inside libc. We therefore
+ * need our own magic version of yp_match() which we can use in any
+ * environment.
+ */
+static int my_yp_match(server, domain, map, key, keylen, result, resultlen)
+       char *server;
+       char *domain;
+       char *map;
+       char *key;
+       unsigned long keylen;
+       char **result;
+       unsigned long *resultlen;
+{
+       ypreq_key ypkey;
+       ypresp_val *ypval;
+       CLIENT *clnt;
+       static char buf[YPMAXRECORD + 2];
+
+       bzero((char *)buf, sizeof(buf));
+
+       if ((clnt = clnt_create(server, YPPROG,YPVERS,"udp")) == NULL) {
+               warnx("failed to create UDP handle: %s",
+                                       clnt_spcreateerror(server));
+               pw_error(tempname, 0, 1);
+       }
+
+       ypkey.domain = domain;
+       ypkey.map = map;
+       ypkey.key.keydat_len = keylen;
+       ypkey.key.keydat_val = key;
+
+       if ((ypval = ypproc_match_2(&ypkey, clnt)) == NULL) {
+               clnt_destroy(clnt);
+               warnx("%s",clnt_sperror(clnt,"YPPROC_MATCH failed"));
+               pw_error(tempname, 0, 1);
+       }
+
+       clnt_destroy(clnt);
+
+       if (ypval->stat != YP_TRUE) {
+               int stat = ypval->stat;
+               xdr_free(xdr_ypresp_val, (char *)ypval);
+               if (stat == YP_NOMAP && strstr(map, "master.passwd"))
+                       return(1);
+               if (stat == YP_NOKEY)
+                       return(1);
+               warnx("ypmatch failed: %s", yperr_string(ypprot_err(stat)));
+               pw_error(tempname, 0, 1);
+       }
+
+
+       strncpy((char *)&buf, ypval->val.valdat_val, ypval->val.valdat_len);
+
+       *result = (char *)&buf;
+       *resultlen = ypval->val.valdat_len;
+
+       xdr_free(xdr_ypresp_val, (char *)ypval);
+
+       return(0);
+}
 
 /*
  * Check if the user we're working with is local or in NIS.
  */
-int use_yp (user)
-char *user;
+int use_yp (user, uid, which)
+       char *user;
+       uid_t uid;
+       int which; /* 0 = use username, 1 = use uid */
 {
-       int yp_enabled = 0, user_not_local = 0, exists = 0;
+       int user_local = 0, user_yp = 0, user_exists = 0;
        DB *dbp;
        DBT key,data;
        char bf[UT_NAMESIZE + 2];
+       char *result;
+       char *server;
+       int resultlen;
+       char ubuf[UT_NAMESIZE + 2];
+
+       if (which) {
+               snprintf(ubuf, sizeof(ubuf), "%lu", uid);
+               user = (char *)&ubuf;
+       }
+
+       /* Grope around for the user in the usual way */
+       if (which) {
+               if (getpwuid(uid) != NULL)
+                       user_exists = 1;
+       } else {
+               if (getpwnam(user) != NULL)
+                       user_exists = 1;
+       }
+
+       /* Now grope directly through the user database */
+       if ((dbp = dbopen(_PATH_SMP_DB, O_RDONLY, PERM_SECURE,
+                       DB_HASH, &openinfo)) == NULL) {
+                       warn("error opening database: %s.", _PATH_MP_DB);
+                       pw_error(tempname, 0, 1);
+       }
 
-       if ((dbp = dbopen(_PATH_MP_DB, O_RDONLY, PERM_SECURE,
-                       DB_HASH, &openinfo)) == NULL)
-                       errx(1, "error opening database: %s.", _PATH_MP_DB);
+       /* Is NIS turned on */
        bf[0] = _PW_KEYYPENABLED;
        key.data = (u_char *)bf;
        key.size = 1;
-       if (!(dbp->get)(dbp,&key,&data,0))
-               yp_enabled = 1;
+       yp_in_pw_file = !(dbp->get)(dbp,&key,&data,0);
+       if (_yp_check(NULL) || (yp_domain && yp_server)) {
+               server = get_yp_master(0);
+
+               /* Is the user in the NIS passwd map */
+               if (!my_yp_match(server, yp_domain, which ? "passwd.byuid" :
+                                       "passwd.byname", user, strlen(user),
+                   &result, &resultlen)) {
+                       user_yp = user_exists = 1;
+                       *(char *)(result + resultlen) = '\0';
+                       copy_yp_pass(result, 0, resultlen);
+               }
+               /* Is the user in the NIS master.passwd map */
+               if (user_yp && !my_yp_match(server, yp_domain, which ? 
+                       "master.passwd.byuid" : "master.passwd.byname", 
+                   user, strlen(user),
+                   &result, &resultlen)) {
+                       *(char *)(result + resultlen) = '\0';
+                       copy_yp_pass(result, 1, resultlen);
+               }
+       }
+
+       /* Is the user in the local password database */
 
-       bf[0] = _PW_KEYBYNAME;
-       bcopy((char *)user, bf + 1, MIN(strlen(user), UT_NAMESIZE));
+       bf[0] = which ? _PW_KEYBYUID : _PW_KEYBYNAME;
+       if (which)
+               bcopy((char *)&uid, bf + 1, sizeof(uid));
+       else
+               bcopy((char *)user, bf + 1, MIN(strlen(user), UT_NAMESIZE));
        key.data = (u_char *)bf;
-       key.size = strlen(user) + 1;
-       if ((dbp->get)(dbp,&key,&data,0))
-               user_not_local = 1;
-       
+       key.size = which ? sizeof(uid) + 1 : strlen(user) + 1;
+       if (!(dbp->get)(dbp,&key,&data,0)) {
+               user_local = 1;
+               copy_local_pass(data.data, data.size);
+       }
+
        (dbp->close)(dbp);
 
-       if (getpwnam(user) != NULL)
-               exists = 1;
+       if (user_local && user_yp && user_exists)
+               return(USER_YP_AND_LOCAL);
+       else if (!user_local && user_yp && user_exists)
+               return(USER_YP_ONLY);
+       else if (user_local && !user_yp && user_exists)
+               return(USER_LOCAL_ONLY);
+       else if (!user_exists)
+               return(USER_UNKNOWN);
 
-       if (yp_enabled && user_not_local && exists)
-               return(1);
-       else
-               return(0);
+       return(-1);
 }
 
 /*
  * Find the name of the NIS master server for this domain
  * and make sure it's running yppasswdd.
  */
-static char *get_yp_master(void)
+char *get_yp_master(getserver)
+       int getserver;
 {
-       char *domain, *mastername;
-       int rval;
+       char *mastername;
+       int rval, localport;
+       struct stat st;
+       char                    *sockname = YP_SOCKNAME;
+
+       /*
+        * Sometimes we are called just to probe for rpc.yppasswdd and
+        * set the suser_override flag. Just return NULL and leave
+        * suser_override at 0 if _use_yp doesn't indicate that NIS is
+        * in use and we weren't called from use_yp() itself.
+        * Without this check, we might try probing and fail with an NIS
+        * error in non-NIS environments.
+        */
+       if ((_use_yp == USER_UNKNOWN || _use_yp == USER_LOCAL_ONLY) &&
+                                                               getserver)
+               return(NULL);
 
        /* Get default NIS domain. */
 
-       if ((rval = yp_get_default_domain(&domain))) {
+       if (yp_domain == NULL && (rval = yp_get_default_domain(&yp_domain))) {
                warnx("can't get local NIS domain name: %s",yperr_string(rval));
-               pw_error(NULL, 0, 1);
+               pw_error(tempname, 0, 1);
        }
 
        /* Get master server of passwd map. */
 
-       if ((rval = yp_master(domain, "passwd.byname", &mastername))) {
-               warnx("can't get master NIS server: %s", yperr_string(rval));
-               pw_error(NULL, 0, 1);
+       if ((mastername = ypxfr_get_master(yp_domain, "passwd.byname",
+                               yp_server, yp_server ? 0 : 1)) == NULL) {
+               warnx("can't get name of master NIS server");
+               pw_error(tempname, 0, 1);
        }
 
+       if (!getserver)
+               return(mastername);
+
        /* Check if yppasswdd is out there. */
 
        if ((rval = getrpcport(mastername, YPPASSWDPROG, YPPASSWDPROC_UPDATE,
                IPPROTO_UDP)) == 0) {
-               warnx("yppasswdd not running on NIS master server");
-               pw_error(NULL, 0, 1);
+               warnx("rpc.yppasswdd is not running on the NIS master server");
+               pw_error(tempname, 0, 1);
        }
 
        /*
@@ -150,91 +384,140 @@ static char *get_yp_master(void)
         */
 
        if (rval >= IPPORT_RESERVED) {
-               warnx("yppasswdd server not running on reserved port");
-               pw_error(NULL, 0, 1);
+               warnx("rpc.yppasswdd server not running on reserved port");
+               pw_error(tempname, 0, 1);
+       }
+
+       /* See if _we_ are the master server. */
+       if (!force_old && !getuid() && (localport = getrpcport("localhost",
+               YPPASSWDPROG, YPPASSWDPROC_UPDATE, IPPROTO_UDP)) != 0) {
+               if (localport == rval && stat(sockname, &st) != -1) {
+                       suser_override = 1;
+                       mastername = "localhost";
+               }
        }
 
        /* Everything checks out: return the name of the server. */
 
        return (mastername);
 }
+
 /*
  * Ask the user for his NIS password and submit the new information
- * to yppasswdd. Note that yppasswdd requires password authentication
+ * to yppasswdd. Note that rpc.yppasswdd requires password authentication
  * and only allows changes to existing records rather than the addition
  * of new records. (To do actual updates we would need something like
- * secure RPC and ypupdated, which FreeBSD doesn't have yet.) This means
- * that the superuser cannot use chpass(1) to add new users records to
- * the NIS password database.
+ * secure RPC and ypupdated, which FreeBSD doesn't have yet.) The FreeBSD
+ * rpc.yppasswdd has some special hooks to allow the superuser update
+ * information without specifying a password, however this only works
+ * for the superuser on the NIS master server.
  */
 void yp_submit(pw)
-struct passwd *pw;
+       struct passwd *pw;
 {
        struct yppasswd yppasswd;
+       struct master_yppasswd master_yppasswd;
        CLIENT *clnt;
-       char *master, *password, *encpass;
-       int rval, status = 0;
-       struct timeval  tv;
+       char *master, *password;
+       int *status = NULL;
+       struct rpc_err err;
+       char                    *sockname = YP_SOCKNAME;
 
-       /* Populate the yppasswd structure that gets handed to yppasswdd. */
-       /*
-        * XXX This is done first to work around what looks like a very
-        * strange memory corruption bug: the text fields pointed to
-        * by the members of the 'pw' structure appear to be clobbered
-        * after get_yp_master() returns (in particular, it happens
-        * during getrpcport()). I don't know exactly where the problem
-        * lies: I traced it all the way to gethostbyname(), then gave
-        * up.
-        */
-       yppasswd.newpw.pw_passwd = strdup(pw->pw_passwd);
-       yppasswd.newpw.pw_name = strdup(pw->pw_name);
-       yppasswd.newpw.pw_uid = pw->pw_uid;
-       yppasswd.newpw.pw_gid = pw->pw_gid;
-       yppasswd.newpw.pw_gecos = strdup(pw->pw_gecos);
-       yppasswd.newpw.pw_dir = strdup(pw->pw_dir);
-       yppasswd.newpw.pw_shell = strdup(pw->pw_shell);
+       _use_yp = 1;
 
        /* Get NIS master server name */
 
-       master = get_yp_master();
+       master = get_yp_master(1);
+
+       /* Populate the yppasswd structure that gets handed to yppasswdd. */
+
+       if (suser_override) {
+               master_yppasswd.newpw.pw_passwd = strdup(pw->pw_passwd);
+               master_yppasswd.newpw.pw_name = strdup(pw->pw_name);
+               master_yppasswd.newpw.pw_uid = pw->pw_uid;
+               master_yppasswd.newpw.pw_gid = pw->pw_gid;
+               master_yppasswd.newpw.pw_expire = pw->pw_expire;
+               master_yppasswd.newpw.pw_change = pw->pw_change;
+               master_yppasswd.newpw.pw_fields = pw->pw_fields;
+               master_yppasswd.newpw.pw_gecos = strdup(pw->pw_gecos);
+               master_yppasswd.newpw.pw_dir = strdup(pw->pw_dir);
+               master_yppasswd.newpw.pw_shell = strdup(pw->pw_shell);
+               master_yppasswd.newpw.pw_class = pw->pw_class != NULL ?
+                                               strdup(pw->pw_class) : "";
+               master_yppasswd.oldpass = ""; /* not really needed */
+               master_yppasswd.domain = yp_domain;
+       } else {
+               yppasswd.newpw.pw_passwd = strdup(pw->pw_passwd);
+               yppasswd.newpw.pw_name = strdup(pw->pw_name);
+               yppasswd.newpw.pw_uid = pw->pw_uid;
+               yppasswd.newpw.pw_gid = pw->pw_gid;
+               yppasswd.newpw.pw_gecos = strdup(pw->pw_gecos);
+               yppasswd.newpw.pw_dir = strdup(pw->pw_dir);
+               yppasswd.newpw.pw_shell = strdup(pw->pw_shell);
+               yppasswd.oldpass = "";
+       }
 
        /* Get the user's password for authentication purposes. */
 
        printf ("Changing NIS information for %s on %s\n",
-                                       yppasswd.newpw.pw_name, master);
-       encpass = (getpwnam(yppasswd.newpw.pw_name))->pw_passwd;
-       password = getpass("Please enter password: ");
-       if (strncmp(crypt(password, encpass), encpass, strlen(encpass))) {
-               warnx("Password incorrect.");
-               pw_error(NULL, 0, 1);
+                                       pw->pw_name, master);
+
+       if (pw->pw_passwd[0] && !suser_override) {
+               password = getpass("Please enter password: ");
+               if (strncmp(crypt(password,pw->pw_passwd),
+                               pw->pw_passwd,strlen(pw->pw_passwd))) {
+                       warnx("Password incorrect.");
+                       pw_error(tempname, 0, 1);
+               }
+               yppasswd.oldpass = password;    /* XXX */
        }
 
-       yppasswd.oldpass = password;    /* XXX */
-
-       /* Create a handle to yppasswdd. */
+       if (suser_override) {
+               /* Talk to server via AF_UNIX socket. */
+               clnt = clnt_create(sockname, MASTER_YPPASSWDPROG,
+                                       MASTER_YPPASSWDVERS, "unix");
+               if (clnt == NULL) {
+                       warnx("failed to contact rpc.yppasswdd: %s",
+                               clnt_spcreateerror(master));
+                       pw_error(tempname, 0, 1);
+               }
+       } else {
+               /* Create a handle to yppasswdd. */
+
+               if ((clnt = clnt_create(master, YPPASSWDPROG,
+                                       YPPASSWDVERS, "udp")) == NULL) {
+                       warnx("failed to contact rpc.yppasswdd: %s",
+                               clnt_spcreateerror(master));
+                       pw_error(tempname, 0, 1);
+               }
+       }
 
-       clnt = clnt_create(master, YPPASSWDPROG, YPPASSWDVERS, "udp");
        clnt->cl_auth = authunix_create_default();
 
-       /* Set a timeout and make the RPC call. */
+       if (suser_override)
+               status = yppasswdproc_update_master_1(&master_yppasswd, clnt);
+       else
+               status = yppasswdproc_update_1(&yppasswd, clnt);
 
-       tv.tv_sec = 20;
-       tv.tv_usec = 0;
-       rval = clnt_call(clnt, YPPASSWDPROC_UPDATE, xdr_yppasswd,
-               (char *)&yppasswd, xdr_int, (char *)&status, &tv);
+       clnt_geterr(clnt, &err);
+
+       auth_destroy(clnt->cl_auth);
+       clnt_destroy(clnt);
 
        /* Call failed: signal the error. */
 
-       if (rval) {
-               warnx("NIS update failed: %s", clnt_sperrno(rval));
+       if (err.re_status != RPC_SUCCESS || status == NULL || *status) {
+               warnx("NIS update failed: %s", clnt_sperrno(err.re_status));
                pw_error(NULL, 0, 1);
        }
 
        /* Success. */
 
-       auth_destroy(clnt->cl_auth);
-       clnt_destroy(clnt);
-       warnx("NIS information changed on host %s", master);
+       if (suser_override)
+               warnx("NIS information changed on host %s, domain %s",
+                       master, yp_domain);
+       else
+               warnx("NIS information changed on host %s", master);
 
        return;
 }