194575Sdes/*- 294575Sdes * Copyright (c) 2002 Networks Associates Technology, Inc. 394575Sdes * All rights reserved. 494575Sdes * 594575Sdes * This software was developed for the FreeBSD Project by ThinkSec AS and 694575Sdes * NAI Labs, the Security Research Division of Network Associates, Inc. 794575Sdes * under DARPA/SPAWAR contract N66001-01-C-8035 ("CBOSS"), as part of the 894575Sdes * DARPA CHATS research program. 994575Sdes * 1094575Sdes * Redistribution and use in source and binary forms, with or without 1194575Sdes * modification, are permitted provided that the following conditions 1294575Sdes * are met: 1394575Sdes * 1. Redistributions of source code must retain the above copyright 1494575Sdes * notice, this list of conditions and the following disclaimer. 1594575Sdes * 2. Redistributions in binary form must reproduce the above copyright 1694575Sdes * notice, this list of conditions and the following disclaimer in the 1794575Sdes * documentation and/or other materials provided with the distribution. 1894575Sdes * 3. The name of the author may not be used to endorse or promote 1994575Sdes * products derived from this software without specific prior written 2094575Sdes * permission. 2194575Sdes * 2294575Sdes * THIS SOFTWARE IS PROVIDED BY THE AUTHOR AND CONTRIBUTORS ``AS IS'' AND 2394575Sdes * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE 2494575Sdes * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE 2594575Sdes * ARE DISCLAIMED. IN NO EVENT SHALL THE AUTHOR OR CONTRIBUTORS BE LIABLE 2694575Sdes * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL 2794575Sdes * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS 2894575Sdes * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) 2994575Sdes * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT 3094575Sdes * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY 3194575Sdes * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF 3294575Sdes * SUCH DAMAGE. 3394575Sdes * 3494575Sdes * $FreeBSD$ 3594575Sdes */ 3694575Sdes 3794577Sdes#include <sys/types.h> 3894577Sdes#include <sys/socket.h> 3994577Sdes#include <netinet/in.h> 4094577Sdes 4194575Sdes#include <err.h> 4294575Sdes#include <errno.h> 4394577Sdes#include <netconfig.h> 4494577Sdes#include <netdb.h> 4594575Sdes#include <pwd.h> 4694575Sdes#include <stdlib.h> 4794575Sdes#include <string.h> 4894577Sdes#include <unistd.h> 4994575Sdes 5094575Sdes#include <rpcsvc/ypclnt.h> 5194575Sdes#include <rpcsvc/yppasswd.h> 5294575Sdes 5394575Sdes#include "ypclnt.h" 5494577Sdes#include "yppasswd_private.h" 5594575Sdes 5694577Sdesstatic int yppasswd_remote(ypclnt_t *, const struct passwd *, const char *); 57116393Smbrstatic int yppasswd_local(ypclnt_t *, const struct passwd *); 5894577Sdes 5996198Sdes/* 6096198Sdes * Determines the availability of rpc.yppasswdd. Returns -1 for not 6196198Sdes * available (or unable to determine), 0 for available, 1 for available in 6296198Sdes * master mode. 6396198Sdes */ 6494575Sdesint 6596198Sdesypclnt_havepasswdd(ypclnt_t *ypclnt) 6694575Sdes{ 67116393Smbr struct netconfig *nc = NULL; 68116393Smbr void *localhandle = 0; 69116393Smbr CLIENT *clnt = NULL; 70116393Smbr int ret; 7194575Sdes 72116393Smbr /* check if rpc.yppasswdd is running */ 7394575Sdes if (getrpcport(ypclnt->server, YPPASSWDPROG, 7494575Sdes YPPASSWDPROC_UPDATE, IPPROTO_UDP) == 0) { 7594575Sdes ypclnt_error(ypclnt, __func__, "no rpc.yppasswdd on server"); 7694575Sdes return (-1); 7794575Sdes } 7894575Sdes 7994577Sdes /* if we're not root, use remote method */ 8094577Sdes if (getuid() != 0) 8196198Sdes return (0); 8294577Sdes 83116393Smbr /* try to connect to rpc.yppasswdd */ 84116393Smbr localhandle = setnetconfig(); 85116393Smbr while ((nc = getnetconfig(localhandle)) != NULL) { 86116393Smbr if (nc->nc_protofmly != NULL && 87116393Smbr strcmp(nc->nc_protofmly, NC_LOOPBACK) == 0) 88116393Smbr break; 8994577Sdes } 90116393Smbr if (nc == NULL) { 91116393Smbr ypclnt_error(ypclnt, __func__, 92116393Smbr "getnetconfig: %s", nc_sperror()); 93116393Smbr ret = 0; 94116393Smbr goto done; 9594577Sdes } 96116393Smbr if ((clnt = clnt_tp_create(NULL, MASTER_YPPASSWDPROG, 97116393Smbr MASTER_YPPASSWDVERS, nc)) == NULL) { 98116393Smbr ypclnt_error(ypclnt, __func__, 99116393Smbr "failed to connect to rpc.yppasswdd: %s", 100116393Smbr clnt_spcreateerror(ypclnt->server)); 101116393Smbr ret = 0; 102116393Smbr goto done; 103116393Smbr } else 104116393Smbr ret = 1; 105116393Smbr 106116393Smbrdone: 107116393Smbr if (clnt != NULL) { 108116393Smbr clnt_destroy(clnt); 109116393Smbr } 110116393Smbr endnetconfig(localhandle); 111116393Smbr return (ret); 11294577Sdes} 11394577Sdes 11494577Sdes/* 11596198Sdes * Updates the NIS user information for the specified user. 11696198Sdes */ 11796198Sdesint 11896198Sdesypclnt_passwd(ypclnt_t *ypclnt, const struct passwd *pwd, const char *passwd) 11996198Sdes{ 12096198Sdes switch (ypclnt_havepasswdd(ypclnt)) { 12196198Sdes case 0: 12296198Sdes return (yppasswd_remote(ypclnt, pwd, passwd)); 12396198Sdes case 1: 124116393Smbr return (yppasswd_local(ypclnt, pwd)); 12596198Sdes default: 12696198Sdes return (-1); 12796198Sdes } 12896198Sdes} 12996198Sdes 13096198Sdes/* 13194577Sdes * yppasswd_remote and yppasswd_local are quite similar but still 13294577Sdes * sufficiently different that merging them into one makes the code 13394577Sdes * significantly less readable, IMHO, so we keep them separate. 13494577Sdes */ 13594577Sdes 13694577Sdesstatic int 137116393Smbryppasswd_local(ypclnt_t *ypclnt, const struct passwd *pwd) 13894577Sdes{ 13994577Sdes struct master_yppasswd yppwd; 14094577Sdes struct rpc_err rpcerr; 14194577Sdes struct netconfig *nc = NULL; 142116393Smbr void *localhandle = 0; 14394577Sdes CLIENT *clnt = NULL; 14494577Sdes int ret, *result; 14594577Sdes 14694577Sdes /* fill the master_yppasswd structure */ 14794577Sdes memset(&yppwd, 0, sizeof yppwd); 14894577Sdes yppwd.newpw.pw_uid = pwd->pw_uid; 14994577Sdes yppwd.newpw.pw_gid = pwd->pw_gid; 15094577Sdes yppwd.newpw.pw_change = pwd->pw_change; 15194577Sdes yppwd.newpw.pw_expire = pwd->pw_expire; 15294577Sdes yppwd.newpw.pw_fields = pwd->pw_fields; 153116393Smbr yppwd.oldpass = strdup(""); 154116393Smbr yppwd.domain = strdup(ypclnt->domain); 15594577Sdes if ((yppwd.newpw.pw_name = strdup(pwd->pw_name)) == NULL || 15694577Sdes (yppwd.newpw.pw_passwd = strdup(pwd->pw_passwd)) == NULL || 15794577Sdes (yppwd.newpw.pw_class = strdup(pwd->pw_class)) == NULL || 15894577Sdes (yppwd.newpw.pw_gecos = strdup(pwd->pw_gecos)) == NULL || 15994577Sdes (yppwd.newpw.pw_dir = strdup(pwd->pw_dir)) == NULL || 160116393Smbr (yppwd.newpw.pw_shell = strdup(pwd->pw_shell)) == NULL) { 16194577Sdes ypclnt_error(ypclnt, __func__, strerror(errno)); 16294577Sdes ret = -1; 16394577Sdes goto done; 16494577Sdes } 16594577Sdes 16694577Sdes /* connect to rpc.yppasswdd */ 167116393Smbr localhandle = setnetconfig(); 168116393Smbr while ((nc = getnetconfig(localhandle)) != NULL) { 169116393Smbr if (nc->nc_protofmly != NULL && 170116393Smbr strcmp(nc->nc_protofmly, NC_LOOPBACK) == 0) 171116393Smbr break; 172116393Smbr } 173116393Smbr if (nc == NULL) { 17494577Sdes ypclnt_error(ypclnt, __func__, 175116393Smbr "getnetconfig: %s", nc_sperror()); 176116393Smbr ret = -1; 177116393Smbr goto done; 178116393Smbr } 179116393Smbr if ((clnt = clnt_tp_create(NULL, MASTER_YPPASSWDPROG, 180116393Smbr MASTER_YPPASSWDVERS, nc)) == NULL) { 181116393Smbr ypclnt_error(ypclnt, __func__, 18294577Sdes "failed to connect to rpc.yppasswdd: %s", 18394577Sdes clnt_spcreateerror(ypclnt->server)); 18494577Sdes ret = -1; 18594577Sdes goto done; 18694577Sdes } 18794577Sdes clnt->cl_auth = authunix_create_default(); 18894577Sdes 18994577Sdes /* request the update */ 19094577Sdes result = yppasswdproc_update_master_1(&yppwd, clnt); 19194577Sdes 19294577Sdes /* check for RPC errors */ 19394577Sdes clnt_geterr(clnt, &rpcerr); 19494577Sdes if (rpcerr.re_status != RPC_SUCCESS) { 19594577Sdes ypclnt_error(ypclnt, __func__, 19694577Sdes "NIS password update failed: %s", 19794577Sdes clnt_sperror(clnt, ypclnt->server)); 19894577Sdes ret = -1; 19994577Sdes goto done; 20094577Sdes } 20194577Sdes 20294577Sdes /* check the result of the update */ 20394577Sdes if (result == NULL || *result != 0) { 20494577Sdes ypclnt_error(ypclnt, __func__, 20594577Sdes "NIS password update failed"); 20694577Sdes /* XXX how do we get more details? */ 20794577Sdes ret = -1; 20894577Sdes goto done; 20994577Sdes } 21094577Sdes 21194577Sdes ypclnt_error(ypclnt, NULL, NULL); 21294577Sdes ret = 0; 21394577Sdes 21494577Sdes done: 21594577Sdes if (clnt != NULL) { 21694577Sdes auth_destroy(clnt->cl_auth); 21794577Sdes clnt_destroy(clnt); 21894577Sdes } 219116393Smbr endnetconfig(localhandle); 22094577Sdes free(yppwd.newpw.pw_name); 22195588Sdes if (yppwd.newpw.pw_passwd != NULL) { 22295588Sdes memset(yppwd.newpw.pw_passwd, 0, strlen(yppwd.newpw.pw_passwd)); 22395588Sdes free(yppwd.newpw.pw_passwd); 22495588Sdes } 22594577Sdes free(yppwd.newpw.pw_class); 22694577Sdes free(yppwd.newpw.pw_gecos); 22794577Sdes free(yppwd.newpw.pw_dir); 22894577Sdes free(yppwd.newpw.pw_shell); 22994577Sdes if (yppwd.oldpass != NULL) { 23094577Sdes memset(yppwd.oldpass, 0, strlen(yppwd.oldpass)); 23194577Sdes free(yppwd.oldpass); 23294577Sdes } 23394577Sdes return (ret); 23494577Sdes} 23594577Sdes 23694577Sdesstatic int 23794577Sdesyppasswd_remote(ypclnt_t *ypclnt, const struct passwd *pwd, const char *passwd) 23894577Sdes{ 23994577Sdes struct yppasswd yppwd; 24094577Sdes struct rpc_err rpcerr; 24194577Sdes CLIENT *clnt = NULL; 24294577Sdes int ret, *result; 24394577Sdes 24494575Sdes /* fill the yppasswd structure */ 24594575Sdes memset(&yppwd, 0, sizeof yppwd); 24694575Sdes yppwd.newpw.pw_uid = pwd->pw_uid; 24794575Sdes yppwd.newpw.pw_gid = pwd->pw_gid; 24894575Sdes if ((yppwd.newpw.pw_name = strdup(pwd->pw_name)) == NULL || 24994575Sdes (yppwd.newpw.pw_passwd = strdup(pwd->pw_passwd)) == NULL || 25094575Sdes (yppwd.newpw.pw_gecos = strdup(pwd->pw_gecos)) == NULL || 25194575Sdes (yppwd.newpw.pw_dir = strdup(pwd->pw_dir)) == NULL || 25294575Sdes (yppwd.newpw.pw_shell = strdup(pwd->pw_shell)) == NULL || 25396198Sdes (yppwd.oldpass = strdup(passwd ? passwd : "")) == NULL) { 25494575Sdes ypclnt_error(ypclnt, __func__, strerror(errno)); 25594575Sdes ret = -1; 25694575Sdes goto done; 25794575Sdes } 25894575Sdes 25994575Sdes /* connect to rpc.yppasswdd */ 26094575Sdes clnt = clnt_create(ypclnt->server, YPPASSWDPROG, YPPASSWDVERS, "udp"); 26194575Sdes if (clnt == NULL) { 26294575Sdes ypclnt_error(ypclnt, __func__, 26394575Sdes "failed to connect to rpc.yppasswdd: %s", 26494575Sdes clnt_spcreateerror(ypclnt->server)); 26594575Sdes ret = -1; 26694575Sdes goto done; 26794575Sdes } 26894575Sdes clnt->cl_auth = authunix_create_default(); 26994575Sdes 27094575Sdes /* request the update */ 27194575Sdes result = yppasswdproc_update_1(&yppwd, clnt); 27294575Sdes 27394575Sdes /* check for RPC errors */ 27494575Sdes clnt_geterr(clnt, &rpcerr); 27594575Sdes if (rpcerr.re_status != RPC_SUCCESS) { 27694575Sdes ypclnt_error(ypclnt, __func__, 27794575Sdes "NIS password update failed: %s", 27894575Sdes clnt_sperror(clnt, ypclnt->server)); 27994575Sdes ret = -1; 28094575Sdes goto done; 28194575Sdes } 28294575Sdes 28394575Sdes /* check the result of the update */ 28494575Sdes if (result == NULL || *result != 0) { 28594575Sdes ypclnt_error(ypclnt, __func__, 28694575Sdes "NIS password update failed"); 28794575Sdes /* XXX how do we get more details? */ 28894575Sdes ret = -1; 28994575Sdes goto done; 29094575Sdes } 29194575Sdes 29294575Sdes ypclnt_error(ypclnt, NULL, NULL); 29394575Sdes ret = 0; 29494575Sdes 29594575Sdes done: 29694575Sdes if (clnt != NULL) { 29794575Sdes auth_destroy(clnt->cl_auth); 29894575Sdes clnt_destroy(clnt); 29994575Sdes } 30094575Sdes free(yppwd.newpw.pw_name); 30195588Sdes if (yppwd.newpw.pw_passwd != NULL) { 30295588Sdes memset(yppwd.newpw.pw_passwd, 0, strlen(yppwd.newpw.pw_passwd)); 30395588Sdes free(yppwd.newpw.pw_passwd); 30495588Sdes } 30594575Sdes free(yppwd.newpw.pw_gecos); 30694575Sdes free(yppwd.newpw.pw_dir); 30794575Sdes free(yppwd.newpw.pw_shell); 30894575Sdes if (yppwd.oldpass != NULL) { 30994575Sdes memset(yppwd.oldpass, 0, strlen(yppwd.oldpass)); 31094575Sdes free(yppwd.oldpass); 31194575Sdes } 31294575Sdes return (ret); 31394575Sdes} 314