ypclnt_passwd.c revision 96198
11541Srgrimes/*- 21541Srgrimes * Copyright (c) 2002 Networks Associates Technology, Inc. 31541Srgrimes * All rights reserved. 41541Srgrimes * 51541Srgrimes * This software was developed for the FreeBSD Project by ThinkSec AS and 61541Srgrimes * NAI Labs, the Security Research Division of Network Associates, Inc. 71541Srgrimes * under DARPA/SPAWAR contract N66001-01-C-8035 ("CBOSS"), as part of the 81541Srgrimes * DARPA CHATS research program. 91541Srgrimes * 101541Srgrimes * Redistribution and use in source and binary forms, with or without 111541Srgrimes * modification, are permitted provided that the following conditions 121541Srgrimes * are met: 131541Srgrimes * 1. Redistributions of source code must retain the above copyright 141541Srgrimes * notice, this list of conditions and the following disclaimer. 151541Srgrimes * 2. Redistributions in binary form must reproduce the above copyright 161541Srgrimes * notice, this list of conditions and the following disclaimer in the 171541Srgrimes * documentation and/or other materials provided with the distribution. 181541Srgrimes * 3. The name of the author may not be used to endorse or promote 191541Srgrimes * products derived from this software without specific prior written 201541Srgrimes * permission. 211541Srgrimes * 221541Srgrimes * THIS SOFTWARE IS PROVIDED BY THE AUTHOR AND CONTRIBUTORS ``AS IS'' AND 231541Srgrimes * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE 241541Srgrimes * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE 251541Srgrimes * ARE DISCLAIMED. IN NO EVENT SHALL THE AUTHOR OR CONTRIBUTORS BE LIABLE 261541Srgrimes * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL 271541Srgrimes * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS 281541Srgrimes * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) 291541Srgrimes * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT 301541Srgrimes * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY 311541Srgrimes * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF 321541Srgrimes * SUCH DAMAGE. 331541Srgrimes * 341541Srgrimes * $FreeBSD: head/lib/libypclnt/ypclnt_passwd.c 96198 2002-05-08 00:48:39Z des $ 351541Srgrimes */ 361541Srgrimes 3710222Sdfr#include <sys/types.h> 381541Srgrimes#include <sys/socket.h> 391541Srgrimes#include <netinet/in.h> 402175Spaul 412175Spaul#include <err.h> 422175Spaul#include <errno.h> 439336Sdfr#include <netconfig.h> 441541Srgrimes#include <netdb.h> 451541Srgrimes#include <pwd.h> 461541Srgrimes#include <stdlib.h> 471541Srgrimes#include <string.h> 481541Srgrimes#include <unistd.h> 491541Srgrimes 501541Srgrimes#include <rpcsvc/ypclnt.h> 511541Srgrimes#include <rpcsvc/yppasswd.h> 521541Srgrimes 5310222Sdfr#include "ypclnt.h" 5410222Sdfr#include "yppasswd_private.h" 5510222Sdfr 5610222Sdfrstatic int yppasswd_remote(ypclnt_t *, const struct passwd *, const char *); 5710222Sdfrstatic int yppasswd_local(ypclnt_t *, const struct passwd *, const char *); 5810222Sdfr 5910222Sdfr/* 601541Srgrimes * Determines the availability of rpc.yppasswdd. Returns -1 for not 611541Srgrimes * available (or unable to determine), 0 for available, 1 for available in 621541Srgrimes * master mode. 631541Srgrimes */ 641541Srgrimesint 651541Srgrimesypclnt_havepasswdd(ypclnt_t *ypclnt) 661541Srgrimes{ 671541Srgrimes struct addrinfo hints, *res; 681541Srgrimes int sd; 691541Srgrimes 701541Srgrimes /* check that rpc.yppasswdd is running */ 711541Srgrimes if (getrpcport(ypclnt->server, YPPASSWDPROG, 721541Srgrimes YPPASSWDPROC_UPDATE, IPPROTO_UDP) == 0) { 731541Srgrimes ypclnt_error(ypclnt, __func__, "no rpc.yppasswdd on server"); 741541Srgrimes return (-1); 751541Srgrimes } 761541Srgrimes 771541Srgrimes /* if we're not root, use remote method */ 781541Srgrimes if (getuid() != 0) 791541Srgrimes return (0); 801541Srgrimes 811541Srgrimes /* try to determine if we are the server */ 821541Srgrimes memset(&hints, 0, sizeof(hints)); 831541Srgrimes hints.ai_family = AF_UNSPEC; 841541Srgrimes hints.ai_socktype = SOCK_STREAM; 851541Srgrimes hints.ai_protocol = 0; 861541Srgrimes if (getaddrinfo(ypclnt->server, NULL, &hints, &res) != 0) 871541Srgrimes return (0); 881541Srgrimes sd = socket(res->ai_family, res->ai_socktype, res->ai_protocol); 891541Srgrimes if (sd == -1) { 901541Srgrimes freeaddrinfo(res); 911541Srgrimes return (0); 921541Srgrimes } 931541Srgrimes if (bind(sd, res->ai_addr, res->ai_addrlen) == -1) { 941541Srgrimes close(sd); 951541Srgrimes freeaddrinfo(res); 961541Srgrimes return (0); 971541Srgrimes } 981541Srgrimes freeaddrinfo(res); 991541Srgrimes close(sd); 1009336Sdfr return (1); 1011541Srgrimes} 1021541Srgrimes 1031541Srgrimes/* 1041541Srgrimes * Updates the NIS user information for the specified user. 1059336Sdfr */ 1069336Sdfrint 1079336Sdfrypclnt_passwd(ypclnt_t *ypclnt, const struct passwd *pwd, const char *passwd) 1089336Sdfr{ 1091541Srgrimes switch (ypclnt_havepasswdd(ypclnt)) { 1109336Sdfr case 0: 1119336Sdfr YPCLNT_DEBUG("using remote update method"); 1129336Sdfr return (yppasswd_remote(ypclnt, pwd, passwd)); 1139336Sdfr case 1: 1149336Sdfr YPCLNT_DEBUG("using local update method"); 1159336Sdfr return (yppasswd_local(ypclnt, pwd, passwd)); 1169336Sdfr default: 1179336Sdfr YPCLNT_DEBUG("no rpc.yppasswdd"); 1189336Sdfr return (-1); 1199336Sdfr } 1209336Sdfr} 1219336Sdfr 1229336Sdfr/* 1239336Sdfr * yppasswd_remote and yppasswd_local are quite similar but still 1249336Sdfr * sufficiently different that merging them into one makes the code 1259336Sdfr * significantly less readable, IMHO, so we keep them separate. 1263305Sphk */ 1273305Sphk 1289336Sdfrstatic int 1299336Sdfryppasswd_local(ypclnt_t *ypclnt, const struct passwd *pwd, const char *passwd) 1309336Sdfr{ 1311541Srgrimes struct master_yppasswd yppwd; 1321541Srgrimes struct rpc_err rpcerr; 1339336Sdfr struct netconfig *nc = NULL; 1349336Sdfr CLIENT *clnt = NULL; 1359336Sdfr int ret, *result; 1369336Sdfr 1379336Sdfr /* fill the master_yppasswd structure */ 1389336Sdfr memset(&yppwd, 0, sizeof yppwd); 1399336Sdfr yppwd.newpw.pw_uid = pwd->pw_uid; 1409336Sdfr yppwd.newpw.pw_gid = pwd->pw_gid; 1419336Sdfr yppwd.newpw.pw_change = pwd->pw_change; 1421541Srgrimes yppwd.newpw.pw_expire = pwd->pw_expire; 1439336Sdfr yppwd.newpw.pw_fields = pwd->pw_fields; 1449336Sdfr if ((yppwd.newpw.pw_name = strdup(pwd->pw_name)) == NULL || 1459336Sdfr (yppwd.newpw.pw_passwd = strdup(pwd->pw_passwd)) == NULL || 1469336Sdfr (yppwd.newpw.pw_class = strdup(pwd->pw_class)) == NULL || 1479336Sdfr (yppwd.newpw.pw_gecos = strdup(pwd->pw_gecos)) == NULL || 1489336Sdfr (yppwd.newpw.pw_dir = strdup(pwd->pw_dir)) == NULL || 1491541Srgrimes (yppwd.newpw.pw_shell = strdup(pwd->pw_shell)) == NULL || 1509336Sdfr (yppwd.oldpass = strdup(passwd ? passwd : "")) == NULL) { 1519336Sdfr ypclnt_error(ypclnt, __func__, strerror(errno)); 1529336Sdfr ret = -1; 1539336Sdfr goto done; 1549336Sdfr } 1559336Sdfr 1569336Sdfr /* connect to rpc.yppasswdd */ 1579336Sdfr nc = getnetconfigent("unix"); 1589336Sdfr clnt = clnt_tp_create(ypclnt->server, YPPASSWDPROG, YPPASSWDVERS, nc); 1599336Sdfr if (clnt == NULL) { 1609336Sdfr ypclnt_error(ypclnt, __func__, 1619336Sdfr "failed to connect to rpc.yppasswdd: %s", 1629336Sdfr clnt_spcreateerror(ypclnt->server)); 1639336Sdfr ret = -1; 1649336Sdfr goto done; 1659336Sdfr } 1661541Srgrimes clnt->cl_auth = authunix_create_default(); 1679336Sdfr 1689336Sdfr /* request the update */ 1699336Sdfr result = yppasswdproc_update_master_1(&yppwd, clnt); 1709336Sdfr 1719336Sdfr /* check for RPC errors */ 1729336Sdfr clnt_geterr(clnt, &rpcerr); 1739336Sdfr if (rpcerr.re_status != RPC_SUCCESS) { 1749336Sdfr ypclnt_error(ypclnt, __func__, 1759336Sdfr "NIS password update failed: %s", 1761541Srgrimes clnt_sperror(clnt, ypclnt->server)); 1771541Srgrimes ret = -1; 1789336Sdfr goto done; 1799336Sdfr } 1809336Sdfr 1819336Sdfr /* check the result of the update */ 1829336Sdfr if (result == NULL || *result != 0) { 1839336Sdfr ypclnt_error(ypclnt, __func__, 1849336Sdfr "NIS password update failed"); 1859336Sdfr /* XXX how do we get more details? */ 1869336Sdfr ret = -1; 1879336Sdfr goto done; 1889336Sdfr } 1899336Sdfr 1909336Sdfr ypclnt_error(ypclnt, NULL, NULL); 1919336Sdfr ret = 0; 1929336Sdfr 1939336Sdfr done: 1949336Sdfr if (clnt != NULL) { 1951541Srgrimes auth_destroy(clnt->cl_auth); 1961541Srgrimes clnt_destroy(clnt); 1971541Srgrimes } 1989336Sdfr if (nc != NULL) 1991541Srgrimes freenetconfigent(nc); 2009336Sdfr free(yppwd.newpw.pw_name); 2019336Sdfr if (yppwd.newpw.pw_passwd != NULL) { 2029336Sdfr memset(yppwd.newpw.pw_passwd, 0, strlen(yppwd.newpw.pw_passwd)); 2039336Sdfr free(yppwd.newpw.pw_passwd); 2049336Sdfr } 2059336Sdfr free(yppwd.newpw.pw_class); 2069336Sdfr free(yppwd.newpw.pw_gecos); 2079336Sdfr free(yppwd.newpw.pw_dir); 2089336Sdfr free(yppwd.newpw.pw_shell); 2099336Sdfr if (yppwd.oldpass != NULL) { 2109336Sdfr memset(yppwd.oldpass, 0, strlen(yppwd.oldpass)); 2119336Sdfr free(yppwd.oldpass); 2129336Sdfr } 2139336Sdfr return (ret); 2149336Sdfr} 2159336Sdfr 2169336Sdfrstatic int 2179336Sdfryppasswd_remote(ypclnt_t *ypclnt, const struct passwd *pwd, const char *passwd) 2189336Sdfr{ 2199336Sdfr struct yppasswd yppwd; 2209336Sdfr struct rpc_err rpcerr; 2219336Sdfr CLIENT *clnt = NULL; 2229336Sdfr int ret, *result; 2239336Sdfr 2249336Sdfr /* fill the yppasswd structure */ 2259336Sdfr memset(&yppwd, 0, sizeof yppwd); 2269336Sdfr yppwd.newpw.pw_uid = pwd->pw_uid; 2279336Sdfr yppwd.newpw.pw_gid = pwd->pw_gid; 2289336Sdfr if ((yppwd.newpw.pw_name = strdup(pwd->pw_name)) == NULL || 2299336Sdfr (yppwd.newpw.pw_passwd = strdup(pwd->pw_passwd)) == NULL || 2309336Sdfr (yppwd.newpw.pw_gecos = strdup(pwd->pw_gecos)) == NULL || 2319336Sdfr (yppwd.newpw.pw_dir = strdup(pwd->pw_dir)) == NULL || 2329336Sdfr (yppwd.newpw.pw_shell = strdup(pwd->pw_shell)) == NULL || 2339336Sdfr (yppwd.oldpass = strdup(passwd ? passwd : "")) == NULL) { 2349336Sdfr ypclnt_error(ypclnt, __func__, strerror(errno)); 2359336Sdfr ret = -1; 2369336Sdfr goto done; 2379336Sdfr } 2389336Sdfr 2399336Sdfr /* connect to rpc.yppasswdd */ 2409336Sdfr clnt = clnt_create(ypclnt->server, YPPASSWDPROG, YPPASSWDVERS, "udp"); 2419336Sdfr if (clnt == NULL) { 2429336Sdfr ypclnt_error(ypclnt, __func__, 2439336Sdfr "failed to connect to rpc.yppasswdd: %s", 2449336Sdfr clnt_spcreateerror(ypclnt->server)); 2459336Sdfr ret = -1; 2469336Sdfr goto done; 2479336Sdfr } 2481541Srgrimes clnt->cl_auth = authunix_create_default(); 2491541Srgrimes 2501541Srgrimes /* request the update */ 2511541Srgrimes result = yppasswdproc_update_1(&yppwd, clnt); 2521541Srgrimes 2531541Srgrimes /* check for RPC errors */ 2541541Srgrimes clnt_geterr(clnt, &rpcerr); 2551541Srgrimes if (rpcerr.re_status != RPC_SUCCESS) { 2561541Srgrimes ypclnt_error(ypclnt, __func__, 2571541Srgrimes "NIS password update failed: %s", 2581541Srgrimes clnt_sperror(clnt, ypclnt->server)); 2591541Srgrimes ret = -1; 2601541Srgrimes goto done; 2611541Srgrimes } 2621541Srgrimes 2639336Sdfr /* check the result of the update */ 2649336Sdfr if (result == NULL || *result != 0) { 2659336Sdfr ypclnt_error(ypclnt, __func__, 2669336Sdfr "NIS password update failed"); 2679336Sdfr /* XXX how do we get more details? */ 2689336Sdfr ret = -1; 2699336Sdfr goto done; 2709336Sdfr } 2719336Sdfr 2729336Sdfr ypclnt_error(ypclnt, NULL, NULL); 2731541Srgrimes ret = 0; 2741541Srgrimes 2759336Sdfr done: 2769336Sdfr if (clnt != NULL) { 2771541Srgrimes auth_destroy(clnt->cl_auth); 2781541Srgrimes clnt_destroy(clnt); 2791541Srgrimes } 2801541Srgrimes free(yppwd.newpw.pw_name); 2811541Srgrimes if (yppwd.newpw.pw_passwd != NULL) { 2829336Sdfr memset(yppwd.newpw.pw_passwd, 0, strlen(yppwd.newpw.pw_passwd)); 2839336Sdfr free(yppwd.newpw.pw_passwd); 2841541Srgrimes } 2851541Srgrimes free(yppwd.newpw.pw_gecos); 2861541Srgrimes free(yppwd.newpw.pw_dir); 2871541Srgrimes free(yppwd.newpw.pw_shell); 2881541Srgrimes if (yppwd.oldpass != NULL) { 2891541Srgrimes memset(yppwd.oldpass, 0, strlen(yppwd.oldpass)); 2901541Srgrimes free(yppwd.oldpass); 2911541Srgrimes } 2928876Srgrimes return (ret); 2931541Srgrimes} 2941541Srgrimes