ypclnt_passwd.c revision 96198
190075Sobrien/*- 2169689Skan * Copyright (c) 2002 Networks Associates Technology, Inc. 390075Sobrien * All rights reserved. 490075Sobrien * 590075Sobrien * This software was developed for the FreeBSD Project by ThinkSec AS and 690075Sobrien * NAI Labs, the Security Research Division of Network Associates, Inc. 790075Sobrien * under DARPA/SPAWAR contract N66001-01-C-8035 ("CBOSS"), as part of the 890075Sobrien * DARPA CHATS research program. 990075Sobrien * 1090075Sobrien * Redistribution and use in source and binary forms, with or without 1190075Sobrien * modification, are permitted provided that the following conditions 12132718Skan * are met: 13132718Skan * 1. Redistributions of source code must retain the above copyright 14132718Skan * notice, this list of conditions and the following disclaimer. 15132718Skan * 2. Redistributions in binary form must reproduce the above copyright 16132718Skan * notice, this list of conditions and the following disclaimer in the 17132718Skan * documentation and/or other materials provided with the distribution. 18132718Skan * 3. The name of the author may not be used to endorse or promote 19132718Skan * products derived from this software without specific prior written 20132718Skan * permission. 2190075Sobrien * 2290075Sobrien * THIS SOFTWARE IS PROVIDED BY THE AUTHOR AND CONTRIBUTORS ``AS IS'' AND 2390075Sobrien * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE 2490075Sobrien * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE 2590075Sobrien * ARE DISCLAIMED. IN NO EVENT SHALL THE AUTHOR OR CONTRIBUTORS BE LIABLE 2690075Sobrien * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL 2790075Sobrien * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS 28169689Skan * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) 29169689Skan * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT 3090075Sobrien * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY 3190075Sobrien * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF 3290075Sobrien * SUCH DAMAGE. 33132718Skan * 34132718Skan * $FreeBSD: head/lib/libypclnt/ypclnt_passwd.c 96198 2002-05-08 00:48:39Z des $ 3590075Sobrien */ 3690075Sobrien 37132718Skan#include <sys/types.h> 38132718Skan#include <sys/socket.h> 39132718Skan#include <netinet/in.h> 4090075Sobrien 4190075Sobrien#include <err.h> 4290075Sobrien#include <errno.h> 43169689Skan#include <netconfig.h> 4490075Sobrien#include <netdb.h> 4590075Sobrien#include <pwd.h> 4690075Sobrien#include <stdlib.h> 4790075Sobrien#include <string.h> 4890075Sobrien#include <unistd.h> 4990075Sobrien 5090075Sobrien#include <rpcsvc/ypclnt.h> 5190075Sobrien#include <rpcsvc/yppasswd.h> 5290075Sobrien 5390075Sobrien#include "ypclnt.h" 5490075Sobrien#include "yppasswd_private.h" 5590075Sobrien 5690075Sobrienstatic int yppasswd_remote(ypclnt_t *, const struct passwd *, const char *); 5790075Sobrienstatic int yppasswd_local(ypclnt_t *, const struct passwd *, const char *); 5890075Sobrien 59132718Skan/* 60132718Skan * Determines the availability of rpc.yppasswdd. Returns -1 for not 61132718Skan * available (or unable to determine), 0 for available, 1 for available in 62132718Skan * master mode. 63117395Skan */ 64117395Skanint 65117395Skanypclnt_havepasswdd(ypclnt_t *ypclnt) 6690075Sobrien{ 6790075Sobrien struct addrinfo hints, *res; 6890075Sobrien int sd; 6990075Sobrien 7090075Sobrien /* check that rpc.yppasswdd is running */ 7190075Sobrien if (getrpcport(ypclnt->server, YPPASSWDPROG, 7290075Sobrien YPPASSWDPROC_UPDATE, IPPROTO_UDP) == 0) { 73169689Skan ypclnt_error(ypclnt, __func__, "no rpc.yppasswdd on server"); 74169689Skan return (-1); 75169689Skan } 76169689Skan 77169689Skan /* if we're not root, use remote method */ 78169689Skan if (getuid() != 0) 79169689Skan return (0); 80169689Skan 8190075Sobrien /* try to determine if we are the server */ 82169689Skan memset(&hints, 0, sizeof(hints)); 8390075Sobrien hints.ai_family = AF_UNSPEC; 8490075Sobrien hints.ai_socktype = SOCK_STREAM; 8590075Sobrien hints.ai_protocol = 0; 86122180Skan if (getaddrinfo(ypclnt->server, NULL, &hints, &res) != 0) 8790075Sobrien return (0); 8890075Sobrien sd = socket(res->ai_family, res->ai_socktype, res->ai_protocol); 8990075Sobrien if (sd == -1) { 9090075Sobrien freeaddrinfo(res); 9190075Sobrien return (0); 9290075Sobrien } 9390075Sobrien if (bind(sd, res->ai_addr, res->ai_addrlen) == -1) { 9490075Sobrien close(sd); 9590075Sobrien freeaddrinfo(res); 9690075Sobrien return (0); 9790075Sobrien } 9890075Sobrien freeaddrinfo(res); 9990075Sobrien close(sd); 10090075Sobrien return (1); 10190075Sobrien} 102169689Skan 103169689Skan/* 104169689Skan * Updates the NIS user information for the specified user. 105169689Skan */ 10690075Sobrienint 10790075Sobrienypclnt_passwd(ypclnt_t *ypclnt, const struct passwd *pwd, const char *passwd) 10890075Sobrien{ 10990075Sobrien switch (ypclnt_havepasswdd(ypclnt)) { 11090075Sobrien case 0: 11190075Sobrien YPCLNT_DEBUG("using remote update method"); 11290075Sobrien return (yppasswd_remote(ypclnt, pwd, passwd)); 11390075Sobrien case 1: 11490075Sobrien YPCLNT_DEBUG("using local update method"); 11590075Sobrien return (yppasswd_local(ypclnt, pwd, passwd)); 11690075Sobrien default: 11790075Sobrien YPCLNT_DEBUG("no rpc.yppasswdd"); 11890075Sobrien return (-1); 11990075Sobrien } 12090075Sobrien} 12190075Sobrien 12290075Sobrien/* 12390075Sobrien * yppasswd_remote and yppasswd_local are quite similar but still 12490075Sobrien * sufficiently different that merging them into one makes the code 12590075Sobrien * significantly less readable, IMHO, so we keep them separate. 12690075Sobrien */ 12790075Sobrien 12890075Sobrienstatic int 12990075Sobrienyppasswd_local(ypclnt_t *ypclnt, const struct passwd *pwd, const char *passwd) 13090075Sobrien{ 13190075Sobrien struct master_yppasswd yppwd; 13290075Sobrien struct rpc_err rpcerr; 133169689Skan struct netconfig *nc = NULL; 134169689Skan CLIENT *clnt = NULL; 135169689Skan int ret, *result; 136169689Skan 137169689Skan /* fill the master_yppasswd structure */ 13890075Sobrien memset(&yppwd, 0, sizeof yppwd); 139169689Skan yppwd.newpw.pw_uid = pwd->pw_uid; 140169689Skan yppwd.newpw.pw_gid = pwd->pw_gid; 141169689Skan yppwd.newpw.pw_change = pwd->pw_change; 142169689Skan yppwd.newpw.pw_expire = pwd->pw_expire; 143169689Skan yppwd.newpw.pw_fields = pwd->pw_fields; 144169689Skan if ((yppwd.newpw.pw_name = strdup(pwd->pw_name)) == NULL || 145169689Skan (yppwd.newpw.pw_passwd = strdup(pwd->pw_passwd)) == NULL || 146169689Skan (yppwd.newpw.pw_class = strdup(pwd->pw_class)) == NULL || 147169689Skan (yppwd.newpw.pw_gecos = strdup(pwd->pw_gecos)) == NULL || 148169689Skan (yppwd.newpw.pw_dir = strdup(pwd->pw_dir)) == NULL || 149169689Skan (yppwd.newpw.pw_shell = strdup(pwd->pw_shell)) == NULL || 150169689Skan (yppwd.oldpass = strdup(passwd ? passwd : "")) == NULL) { 151169689Skan ypclnt_error(ypclnt, __func__, strerror(errno)); 152169689Skan ret = -1; 153169689Skan goto done; 154169689Skan } 155169689Skan 15690075Sobrien /* connect to rpc.yppasswdd */ 15790075Sobrien nc = getnetconfigent("unix"); 15890075Sobrien clnt = clnt_tp_create(ypclnt->server, YPPASSWDPROG, YPPASSWDVERS, nc); 159132718Skan if (clnt == NULL) { 160132718Skan ypclnt_error(ypclnt, __func__, 161132718Skan "failed to connect to rpc.yppasswdd: %s", 162146895Skan clnt_spcreateerror(ypclnt->server)); 163146895Skan ret = -1; 164146895Skan goto done; 165146895Skan } 166146895Skan clnt->cl_auth = authunix_create_default(); 167132718Skan 168169689Skan /* request the update */ 169132718Skan result = yppasswdproc_update_master_1(&yppwd, clnt); 170132718Skan 171132718Skan /* check for RPC errors */ 172169689Skan clnt_geterr(clnt, &rpcerr); 173169689Skan if (rpcerr.re_status != RPC_SUCCESS) { 174169689Skan ypclnt_error(ypclnt, __func__, 17590075Sobrien "NIS password update failed: %s", 176132718Skan clnt_sperror(clnt, ypclnt->server)); 177132718Skan ret = -1; 178169689Skan goto done; 179169689Skan } 180169689Skan 181169689Skan /* check the result of the update */ 182169689Skan if (result == NULL || *result != 0) { 18390075Sobrien ypclnt_error(ypclnt, __func__, 18490075Sobrien "NIS password update failed"); 185132718Skan /* XXX how do we get more details? */ 186132718Skan ret = -1; 187132718Skan goto done; 188132718Skan } 189132718Skan 190132718Skan ypclnt_error(ypclnt, NULL, NULL); 191117395Skan ret = 0; 192117395Skan 193117395Skan done: 194117395Skan if (clnt != NULL) { 195117395Skan auth_destroy(clnt->cl_auth); 196132718Skan clnt_destroy(clnt); 197117395Skan } 198117395Skan if (nc != NULL) 199169689Skan freenetconfigent(nc); 20090075Sobrien free(yppwd.newpw.pw_name); 20190075Sobrien if (yppwd.newpw.pw_passwd != NULL) { 20290075Sobrien memset(yppwd.newpw.pw_passwd, 0, strlen(yppwd.newpw.pw_passwd)); 20390075Sobrien free(yppwd.newpw.pw_passwd); 204132718Skan } 205132718Skan free(yppwd.newpw.pw_class); 206132718Skan free(yppwd.newpw.pw_gecos); 207132718Skan free(yppwd.newpw.pw_dir); 208169689Skan free(yppwd.newpw.pw_shell); 209132718Skan if (yppwd.oldpass != NULL) { 210169689Skan memset(yppwd.oldpass, 0, strlen(yppwd.oldpass)); 211169689Skan free(yppwd.oldpass); 212169689Skan } 213169689Skan return (ret); 214169689Skan} 215169689Skan 216169689Skanstatic int 217132718Skanyppasswd_remote(ypclnt_t *ypclnt, const struct passwd *pwd, const char *passwd) 218132718Skan{ 219132718Skan struct yppasswd yppwd; 220132718Skan struct rpc_err rpcerr; 221132718Skan CLIENT *clnt = NULL; 222169689Skan int ret, *result; 223169689Skan 224169689Skan /* fill the yppasswd structure */ 225169689Skan memset(&yppwd, 0, sizeof yppwd); 22690075Sobrien yppwd.newpw.pw_uid = pwd->pw_uid; 22790075Sobrien yppwd.newpw.pw_gid = pwd->pw_gid; 228132718Skan if ((yppwd.newpw.pw_name = strdup(pwd->pw_name)) == NULL || 229132718Skan (yppwd.newpw.pw_passwd = strdup(pwd->pw_passwd)) == NULL || 230132718Skan (yppwd.newpw.pw_gecos = strdup(pwd->pw_gecos)) == NULL || 231132718Skan (yppwd.newpw.pw_dir = strdup(pwd->pw_dir)) == NULL || 232132718Skan (yppwd.newpw.pw_shell = strdup(pwd->pw_shell)) == NULL || 233132718Skan (yppwd.oldpass = strdup(passwd ? passwd : "")) == NULL) { 234169689Skan ypclnt_error(ypclnt, __func__, strerror(errno)); 235169689Skan ret = -1; 236132718Skan goto done; 237132718Skan } 238132718Skan 239132718Skan /* connect to rpc.yppasswdd */ 240132718Skan clnt = clnt_create(ypclnt->server, YPPASSWDPROG, YPPASSWDVERS, "udp"); 241132718Skan if (clnt == NULL) { 242132718Skan ypclnt_error(ypclnt, __func__, 243132718Skan "failed to connect to rpc.yppasswdd: %s", 244132718Skan clnt_spcreateerror(ypclnt->server)); 245169689Skan ret = -1; 246169689Skan goto done; 247132718Skan } 248132718Skan clnt->cl_auth = authunix_create_default(); 249132718Skan 250169689Skan /* request the update */ 251169689Skan result = yppasswdproc_update_1(&yppwd, clnt); 252169689Skan 253169689Skan /* check for RPC errors */ 254169689Skan clnt_geterr(clnt, &rpcerr); 255169689Skan if (rpcerr.re_status != RPC_SUCCESS) { 256169689Skan ypclnt_error(ypclnt, __func__, 257169689Skan "NIS password update failed: %s", 258169689Skan clnt_sperror(clnt, ypclnt->server)); 259169689Skan ret = -1; 260169689Skan goto done; 261169689Skan } 262169689Skan 263169689Skan /* check the result of the update */ 264169689Skan if (result == NULL || *result != 0) { 265169689Skan ypclnt_error(ypclnt, __func__, 266169689Skan "NIS password update failed"); 267169689Skan /* XXX how do we get more details? */ 268169689Skan ret = -1; 269169689Skan goto done; 270169689Skan } 271169689Skan 272169689Skan ypclnt_error(ypclnt, NULL, NULL); 273169689Skan ret = 0; 27490075Sobrien 27590075Sobrien done: 27690075Sobrien if (clnt != NULL) { 27790075Sobrien auth_destroy(clnt->cl_auth); 27890075Sobrien clnt_destroy(clnt); 27990075Sobrien } 28090075Sobrien free(yppwd.newpw.pw_name); 28190075Sobrien if (yppwd.newpw.pw_passwd != NULL) { 282169689Skan memset(yppwd.newpw.pw_passwd, 0, strlen(yppwd.newpw.pw_passwd)); 283169689Skan free(yppwd.newpw.pw_passwd); 284169689Skan } 285169689Skan free(yppwd.newpw.pw_gecos); 286169689Skan free(yppwd.newpw.pw_dir); 287169689Skan free(yppwd.newpw.pw_shell); 288169689Skan if (yppwd.oldpass != NULL) { 289169689Skan memset(yppwd.oldpass, 0, strlen(yppwd.oldpass)); 290169689Skan free(yppwd.oldpass); 291169689Skan } 29290075Sobrien return (ret); 29390075Sobrien} 29490075Sobrien