Goal: Make rpc.yppasswdd happy again. Testers are welcome, fixes too. 1.) include/rpc/svc.h lib/libc/rpc/svc_vc.c Replace the old SCM_CREDS cred procedures. They can now be replaced just fine with getpeereid(), and the whole code is a lot simpler. We don't break the ABI, since all server programms use __rpc_get_local_uid(), and we just change library internals. 2.) usr.sbin/rpc.yppasswdd/yppasswdd_server.c Only call pw_mkdb if passfile == _PATH_MASTERPASSWD. Otherwise, rename master.passwd to a temp filename, rename the new passwd to master.passwd, and let yppwupdate update passwd as it sees fit. This is PR 52601, with some additional style fixes. Thank you Dan ! 3.) lib/libpam/modules/pam_unix/pam_unix.c Fix the master yppasswd routines, so they really work for root on ypmaster. yppasswd_local() did use YPPASSWDPROG instead of MASTER_YPPASSWDPROG, and the domain was not set, resulting in a coredump during xdr-encode. 4.) lib/libypclnt/ypclnt_passwd.c Fix the master yppasswd routines, so they really work for root on ypmaster. ypclnt_havepasswdd() was buggy. --- include/rpc/svc.h.orig Sat Jun 14 21:38:51 2003 +++ include/rpc/svc.h Sat Jun 14 21:39:15 2003 @@ -144,11 +144,6 @@ #define svc_getrpccaller(x) (&(x)->xp_rtaddr) /* - * FreeBSD-only definition to get the creds of the caller (AF_LOCAL). - */ -#define __svc_getcallercreds(x) ((struct cmsgcred *)(x)->xp_p2) - -/* * Operations defined on an SVCXPRT handle * * SVCXPRT *xprt; --- lib/libc/rpc/svc_vc.c.orig Sat Jun 14 13:28:43 2003 +++ lib/libc/rpc/svc_vc.c Sat Jun 14 13:31:05 2003 @@ -70,11 +71,6 @@ #include "rpc_com.h" #include "un-namespace.h" -struct cmessage { - struct cmsghdr cmsg; - struct cmsgcred cmcred; -}; - extern rwlock_t svc_fd_lock; static SVCXPRT *makefd_xprt(int, u_int, u_int); @@ -94,7 +90,6 @@ static bool_t svc_vc_control(SVCXPRT *xprt, const u_int rq, void *in); static bool_t svc_vc_rendezvous_control (SVCXPRT *xprt, const u_int rq, void *in); -static int __msgread_withcred(int, void *, size_t, struct cmessage *); static int __msgwrite(int, void *, size_t); struct cf_rendezvous { /* kept in xprt->xp_p1 for rendezvouser */ @@ -476,8 +471,6 @@ int sock; int milliseconds = 35 * 1000; struct pollfd pollfd; - struct sockaddr *sa; - struct cmessage *cm; struct cf_conn *cfp; xprt = (SVCXPRT *)xprtp; @@ -487,15 +480,8 @@ cfp = (struct cf_conn *)xprt->xp_p1; - cm = NULL; - sa = (struct sockaddr *)xprt->xp_rtaddr.buf; if (cfp->nonblock) { - if (sa->sa_family == AF_LOCAL) { - cm = (struct cmessage *)xprt->xp_verf.oa_base; - if ((len = __msgread_withcred(sock, buf, len, cm)) > 0) - xprt->xp_p2 = &cm->cmcred; - } else - len = _read(sock, buf, (size_t)len); + len = _read(sock, buf, (size_t)len); if (len < 0) { if (errno == EAGAIN) len = 0; @@ -524,18 +510,9 @@ } } while ((pollfd.revents & POLLIN) == 0); - if (sa->sa_family == AF_LOCAL) { - cm = (struct cmessage *)xprt->xp_verf.oa_base; - if ((len = __msgread_withcred(sock, buf, len, cm)) > 0) { - xprt->xp_p2 = &cm->cmcred; - return (len); - } else - goto fatal_err; - } else { - if ((len = _read(sock, buf, (size_t)len)) > 0) { - gettimeofday(&cfp->last_recv_time, NULL); - return (len); - } + if ((len = _read(sock, buf, (size_t)len)) > 0) { + gettimeofday(&cfp->last_recv_time, NULL); + return (len); } fatal_err: @@ -555,7 +532,6 @@ { SVCXPRT *xprt; int i, cnt; - struct sockaddr *sa; struct cf_conn *cd; struct timeval tv0, tv1; @@ -567,12 +543,8 @@ if (cd->nonblock) gettimeofday(&tv0, NULL); - sa = (struct sockaddr *)xprt->xp_rtaddr.buf; for (cnt = len; cnt > 0; cnt -= i, buf += i) { - if (sa->sa_family == AF_LOCAL) - i = __msgwrite(xprt->xp_fd, buf, (size_t)cnt); - else - i = _write(xprt->xp_fd, buf, (size_t)cnt); + i = _write(xprt->xp_fd, buf, (size_t)cnt); if (i < 0) { if (errno != EAGAIN || !cd->nonblock) { cd->strm_stat = XPRT_DIED; @@ -747,98 +719,26 @@ mutex_unlock(&ops_lock); } -int -__msgread_withcred(sock, buf, cnt, cmp) - int sock; - void *buf; - size_t cnt; - struct cmessage *cmp; -{ - struct iovec iov[1]; - struct msghdr msg; - union { - struct cmsghdr cmsg; - char control[CMSG_SPACE(sizeof(struct cmsgcred))]; - } cm; - int ret; - - - bzero(&cm, sizeof(cm)); - iov[0].iov_base = buf; - iov[0].iov_len = cnt; - - msg.msg_iov = iov; - msg.msg_iovlen = 1; - msg.msg_name = NULL; - msg.msg_namelen = 0; - msg.msg_control = &cm; - msg.msg_controllen = CMSG_SPACE(sizeof(struct cmsgcred)); - msg.msg_flags = 0; - - ret = _recvmsg(sock, &msg, 0); - bcopy(&cm.cmsg, &cmp->cmsg, sizeof(cmp->cmsg)); - bcopy(CMSG_DATA(&cm), &cmp->cmcred, sizeof(cmp->cmcred)); - - if ((msg.msg_flags & MSG_CTRUNC) != 0) - return (-1); - - return (ret); -} - -static int -__msgwrite(sock, buf, cnt) - int sock; - void *buf; - size_t cnt; -{ - struct iovec iov[1]; - struct msghdr msg; - struct cmessage cm; - - bzero((char *)&cm, sizeof(cm)); - iov[0].iov_base = buf; - iov[0].iov_len = cnt; - - cm.cmsg.cmsg_type = SCM_CREDS; - cm.cmsg.cmsg_level = SOL_SOCKET; - cm.cmsg.cmsg_len = sizeof(struct cmessage); - - msg.msg_iov = iov; - msg.msg_iovlen = 1; - msg.msg_name = NULL; - msg.msg_namelen = 0; - msg.msg_control = &cm; - msg.msg_controllen = sizeof(struct cmessage); - msg.msg_flags = 0; - - return(_sendmsg(sock, &msg, 0)); -} - /* - * Get the effective UID of the sending process. Used by rpcbind and keyserv - * (AF_LOCAL). + * Get the effective UID of the sending process. Used by rpcbind, keyserv + * and rpc.yppasswdd on AF_LOCAL. */ int -__rpc_get_local_uid(SVCXPRT *transp, uid_t *uid) -{ - struct cmsgcred *cmcred; - struct cmessage *cm; - struct cmsghdr *cmp; - - cm = (struct cmessage *)transp->xp_verf.oa_base; - - if (cm == NULL) - return (-1); - cmp = &cm->cmsg; - if (cmp == NULL || cmp->cmsg_level != SOL_SOCKET || - cmp->cmsg_type != SCM_CREDS) +__rpc_get_local_uid(SVCXPRT *transp, uid_t *uid) { + int sock, ret; + gid_t egid; + uid_t euid; + struct sockaddr *sa; + + sock = transp->xp_fd; + sa = (struct sockaddr *)transp->xp_rtaddr.buf; + if (sa->sa_family == AF_LOCAL) { + ret = getpeereid(sock, &euid, &egid); + if (ret == 0) + *uid = euid; + return (ret); + } else return (-1); - - cmcred = __svc_getcallercreds(transp); - if (cmcred == NULL) - return (-1); - *uid = cmcred->cmcred_euid; - return (0); } /* --- usr.sbin/rpc.yppasswdd/yppasswdd_server.c.orig Sat May 3 23:06:39 2003 +++ usr.sbin/rpc.yppasswdd/yppasswdd_server.c Sat Jun 14 20:06:03 2003 @@ -448,6 +448,7 @@ char *oldgecos = NULL; char *passfile_hold; char passfile_buf[MAXPATHLEN + 2]; + char passfile_hold_buf[MAXPATHLEN + 2]; char *domain = yppasswd_domain; static struct sockaddr_in clntaddr; static struct timeval t_saved, t_test; @@ -572,6 +573,15 @@ passfile = (char *)&passfile_buf; } + /* + * Create a filename to hold the original master.passwd + * so if our call to yppwupdate fails we can roll back + */ + snprintf(passfile_hold_buf, sizeof(passfile_hold_buf), + "%s.hold", passfile); + passfile_hold = (char *)&passfile_hold_buf; + + /* Step 5: make a new password file with the updated info. */ if (pw_init(dirname(passfile), passfile)) { @@ -593,11 +603,38 @@ yp_error("pw_copy() failed"); return &result; } - if (pw_mkdb(yp_password.pw_name) == -1) { + if (rename(passfile, passfile_hold) == -1) { pw_fini(); - yp_error("pw_mkdb() failed"); + yp_error("rename of %s to %s failed", passfile, + passfile_hold); return &result; } + + if (strcmp(passfile, _PATH_MASTERPASSWD) == 0) { + /* + * NIS server is exporting the system's master.passwd. + * Call pw_mkdb to rebuild passwd and the .db files + */ + if (pw_mkdb(yp_password.pw_name) == -1) { + pw_fini(); + yp_error("pw_mkdb() failed"); + rename(passfile_hold, passfile); + return &result; + } + } else { + /* + * NIS server is exporting a private master.passwd. + * Rename tempfile into final location + */ + if (rename(pw_tempname(), passfile) == -1) { + pw_fini(); + yp_error("rename of %s to %s failed", + pw_tempname(), passfile); + rename(passfile_hold, passfile); + return &result; + } + } + pw_fini(); if (inplace) { @@ -633,9 +670,8 @@ } if (verbose) { - yp_error("update completed for user %s (uid %d):", - argp->newpw.pw_name, - argp->newpw.pw_uid); + yp_error("update completed for user %s (uid %d) in %s:", + argp->newpw.pw_name, argp->newpw.pw_uid, passfile); if (passwd_changed) yp_error("password changed"); @@ -780,10 +816,12 @@ yp_error("pw_copy() failed"); return &result; } - if (pw_mkdb(argp->newpw.pw_name) == -1) { - pw_fini(); - yp_error("pw_mkdb() failed"); - return &result; + if (strcmp(passfile, _PATH_MASTERPASSWD) == 0) { + if (pw_mkdb(argp->newpw.pw_name) == -1) { + pw_fini(); + yp_error("pw_mkdb() failed"); + return &result; + } } pw_fini(); --- lib/libpam/modules/pam_unix/pam_unix.c.orig Sat May 31 19:19:03 2003 +++ lib/libpam/modules/pam_unix/pam_unix.c Sat Jun 14 20:05:51 2003 @@ -292,7 +292,33 @@ (pwd->pw_fields & _PWF_SOURCE) == _PWF_FILES) /* root doesn't need the old password */ return (pam_set_item(pamh, PAM_OLDAUTHTOK, "")); +#ifdef YP + if (getuid() == 0 && + (pwd->pw_fields & _PWF_SOURCE) == _PWF_NIS) { + yp_domain = yp_server = NULL; + (void)pam_get_data(pamh, + "yp_domain", (const void **)&yp_domain); + (void)pam_get_data(pamh, + "yp_server", (const void **)&yp_server); + + ypclnt = ypclnt_new(yp_domain, "passwd.byname", yp_server); + if (ypclnt == NULL) + return (PAM_BUF_ERR); + + if (ypclnt_connect(ypclnt) == -1) { + ypclnt_free(ypclnt); + return (PAM_SERVICE_ERR); + } + + retval = ypclnt_havepasswdd(ypclnt); + ypclnt_free(ypclnt); + if (retval == 1) + return (pam_set_item(pamh, PAM_OLDAUTHTOK, "")); + else if (retval == -1) + return (PAM_SERVICE_ERR); + } +#endif if (pwd->pw_passwd[0] == '\0' && openpam_get_option(pamh, PAM_OPT_NULLOK)) { /* --- lib/libypclnt/ypclnt_passwd.c.orig Mon Dec 16 23:24:25 2002 +++ lib/libypclnt/ypclnt_passwd.c Sat Jun 14 20:06:13 2003 @@ -54,7 +54,7 @@ #include "yppasswd_private.h" static int yppasswd_remote(ypclnt_t *, const struct passwd *, const char *); -static int yppasswd_local(ypclnt_t *, const struct passwd *, const char *); +static int yppasswd_local(ypclnt_t *, const struct passwd *); /* * Determines the availability of rpc.yppasswdd. Returns -1 for not @@ -64,10 +64,12 @@ int ypclnt_havepasswdd(ypclnt_t *ypclnt) { - struct addrinfo hints, *res; - int sd; + struct netconfig *nc = NULL; + void *localhandle = 0; + CLIENT *clnt = NULL; + int ret; - /* check that rpc.yppasswdd is running */ + /* check if rpc.yppasswdd is running */ if (getrpcport(ypclnt->server, YPPASSWDPROG, YPPASSWDPROC_UPDATE, IPPROTO_UDP) == 0) { ypclnt_error(ypclnt, __func__, "no rpc.yppasswdd on server"); @@ -78,26 +80,35 @@ if (getuid() != 0) return (0); - /* try to determine if we are the server */ - memset(&hints, 0, sizeof(hints)); - hints.ai_family = AF_UNSPEC; - hints.ai_socktype = SOCK_STREAM; - hints.ai_protocol = 0; - if (getaddrinfo(ypclnt->server, NULL, &hints, &res) != 0) - return (0); - sd = socket(res->ai_family, res->ai_socktype, res->ai_protocol); - if (sd == -1) { - freeaddrinfo(res); - return (0); + /* try to connect to rpc.yppasswdd */ + localhandle = setnetconfig(); + while ((nc = getnetconfig(localhandle)) != NULL) { + if (nc->nc_protofmly != NULL && + strcmp(nc->nc_protofmly, NC_LOOPBACK) == 0) + break; } - if (bind(sd, res->ai_addr, res->ai_addrlen) == -1) { - close(sd); - freeaddrinfo(res); - return (0); + if (nc == NULL) { + ypclnt_error(ypclnt, __func__, + "getnetconfig: %s", nc_sperror()); + ret = 0; + goto done; + } + if ((clnt = clnt_tp_create(NULL, MASTER_YPPASSWDPROG, + MASTER_YPPASSWDVERS, nc)) == NULL) { + ypclnt_error(ypclnt, __func__, + "failed to connect to rpc.yppasswdd: %s", + clnt_spcreateerror(ypclnt->server)); + ret = 0; + goto done; + } else + ret = 1; + +done: + if (clnt != NULL) { + clnt_destroy(clnt); } - freeaddrinfo(res); - close(sd); - return (1); + endnetconfig(localhandle); + return (ret); } /* @@ -112,7 +123,7 @@ return (yppasswd_remote(ypclnt, pwd, passwd)); case 1: YPCLNT_DEBUG("using local update method"); - return (yppasswd_local(ypclnt, pwd, passwd)); + return (yppasswd_local(ypclnt, pwd)); default: YPCLNT_DEBUG("no rpc.yppasswdd"); return (-1); @@ -126,11 +137,12 @@ */ static int -yppasswd_local(ypclnt_t *ypclnt, const struct passwd *pwd, const char *passwd) +yppasswd_local(ypclnt_t *ypclnt, const struct passwd *pwd) { struct master_yppasswd yppwd; struct rpc_err rpcerr; struct netconfig *nc = NULL; + void *localhandle = 0; CLIENT *clnt = NULL; int ret, *result; @@ -141,24 +153,34 @@ yppwd.newpw.pw_change = pwd->pw_change; yppwd.newpw.pw_expire = pwd->pw_expire; yppwd.newpw.pw_fields = pwd->pw_fields; + yppwd.oldpass = strdup(""); + yppwd.domain = strdup(ypclnt->domain); if ((yppwd.newpw.pw_name = strdup(pwd->pw_name)) == NULL || (yppwd.newpw.pw_passwd = strdup(pwd->pw_passwd)) == NULL || (yppwd.newpw.pw_class = strdup(pwd->pw_class)) == NULL || (yppwd.newpw.pw_gecos = strdup(pwd->pw_gecos)) == NULL || (yppwd.newpw.pw_dir = strdup(pwd->pw_dir)) == NULL || - (yppwd.newpw.pw_shell = strdup(pwd->pw_shell)) == NULL || - (yppwd.oldpass = strdup(passwd ? passwd : "")) == NULL) { + (yppwd.newpw.pw_shell = strdup(pwd->pw_shell)) == NULL) { ypclnt_error(ypclnt, __func__, strerror(errno)); ret = -1; goto done; } /* connect to rpc.yppasswdd */ - nc = getnetconfigent("local"); - if (nc == NULL) - nc = getnetconfigent("unix"); - clnt = clnt_tp_create(ypclnt->server, YPPASSWDPROG, YPPASSWDVERS, nc); - if (clnt == NULL) { + localhandle = setnetconfig(); + while ((nc = getnetconfig(localhandle)) != NULL) { + if (nc->nc_protofmly != NULL && + strcmp(nc->nc_protofmly, NC_LOOPBACK) == 0) + break; + } + if (nc == NULL) { + ypclnt_error(ypclnt, __func__, + "getnetconfig: %s", nc_sperror()); + ret = -1; + goto done; + } + if ((clnt = clnt_tp_create(NULL, MASTER_YPPASSWDPROG, + MASTER_YPPASSWDVERS, nc)) == NULL) { ypclnt_error(ypclnt, __func__, "failed to connect to rpc.yppasswdd: %s", clnt_spcreateerror(ypclnt->server)); @@ -197,8 +219,7 @@ auth_destroy(clnt->cl_auth); clnt_destroy(clnt); } - if (nc != NULL) - freenetconfigent(nc); + endnetconfig(localhandle); free(yppwd.newpw.pw_name); if (yppwd.newpw.pw_passwd != NULL) { memset(yppwd.newpw.pw_passwd, 0, strlen(yppwd.newpw.pw_passwd));