1*730fdfa6Sandvar /* $NetBSD: pam_unix.c,v 1.19 2022/10/26 22:09:37 andvar Exp $ */
2bb62ec41Schristos
36f11bdf1Schristos /*-
46f11bdf1Schristos * Copyright 1998 Juniper Networks, Inc.
56f11bdf1Schristos * All rights reserved.
66f11bdf1Schristos * Copyright (c) 2002-2003 Networks Associates Technology, Inc.
76f11bdf1Schristos * All rights reserved.
86f11bdf1Schristos *
96f11bdf1Schristos * Portions of this software was developed for the FreeBSD Project by
106f11bdf1Schristos * ThinkSec AS and NAI Labs, the Security Research Division of Network
116f11bdf1Schristos * Associates, Inc. under DARPA/SPAWAR contract N66001-01-C-8035
126f11bdf1Schristos * ("CBOSS"), as part of the DARPA CHATS research program.
136f11bdf1Schristos *
146f11bdf1Schristos * Redistribution and use in source and binary forms, with or without
156f11bdf1Schristos * modification, are permitted provided that the following conditions
166f11bdf1Schristos * are met:
176f11bdf1Schristos * 1. Redistributions of source code must retain the above copyright
186f11bdf1Schristos * notice, this list of conditions and the following disclaimer.
196f11bdf1Schristos * 2. Redistributions in binary form must reproduce the above copyright
206f11bdf1Schristos * notice, this list of conditions and the following disclaimer in the
216f11bdf1Schristos * documentation and/or other materials provided with the distribution.
226f11bdf1Schristos * 3. The name of the author may not be used to endorse or promote
236f11bdf1Schristos * products derived from this software without specific prior written
246f11bdf1Schristos * permission.
256f11bdf1Schristos *
266f11bdf1Schristos * THIS SOFTWARE IS PROVIDED BY THE AUTHOR AND CONTRIBUTORS ``AS IS'' AND
276f11bdf1Schristos * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
286f11bdf1Schristos * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
296f11bdf1Schristos * ARE DISCLAIMED. IN NO EVENT SHALL THE AUTHOR OR CONTRIBUTORS BE LIABLE
306f11bdf1Schristos * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
316f11bdf1Schristos * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS
326f11bdf1Schristos * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION)
336f11bdf1Schristos * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT
346f11bdf1Schristos * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY
356f11bdf1Schristos * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF
366f11bdf1Schristos * SUCH DAMAGE.
376f11bdf1Schristos */
386f11bdf1Schristos
396f11bdf1Schristos #include <sys/cdefs.h>
40bb62ec41Schristos #ifdef __FreeBSD__
416f11bdf1Schristos __FBSDID("$FreeBSD: src/lib/libpam/modules/pam_unix/pam_unix.c,v 1.49 2004/02/10 10:13:21 des Exp $");
42bb62ec41Schristos #else
43*730fdfa6Sandvar __RCSID("$NetBSD: pam_unix.c,v 1.19 2022/10/26 22:09:37 andvar Exp $");
44bb62ec41Schristos #endif
456f11bdf1Schristos
466f11bdf1Schristos
47bb62ec41Schristos #include <sys/types.h>
48bb62ec41Schristos
49bb62ec41Schristos #include <ctype.h>
50bb62ec41Schristos #include <errno.h>
51bb62ec41Schristos #include <fcntl.h>
526f11bdf1Schristos #include <pwd.h>
53bb62ec41Schristos #include <grp.h>
54bb62ec41Schristos #include <limits.h>
556f11bdf1Schristos #include <stdlib.h>
566f11bdf1Schristos #include <string.h>
576f11bdf1Schristos #include <stdio.h>
58bb62ec41Schristos #include <login_cap.h>
59bb62ec41Schristos #include <time.h>
60bb62ec41Schristos #include <tzfile.h>
616f11bdf1Schristos #include <unistd.h>
626f11bdf1Schristos
63bb62ec41Schristos #include <util.h>
646f11bdf1Schristos
656f11bdf1Schristos #ifdef YP
66bb62ec41Schristos #include <rpc/rpc.h>
67bb62ec41Schristos #include <rpcsvc/ypclnt.h>
68bb62ec41Schristos #include <rpcsvc/yppasswd.h>
696f11bdf1Schristos #endif
706f11bdf1Schristos
716f11bdf1Schristos #define PAM_SM_AUTH
726f11bdf1Schristos #define PAM_SM_ACCOUNT
736f11bdf1Schristos #define PAM_SM_PASSWORD
746f11bdf1Schristos
756f11bdf1Schristos #include <security/pam_appl.h>
766f11bdf1Schristos #include <security/pam_modules.h>
776f11bdf1Schristos #include <security/pam_mod_misc.h>
786f11bdf1Schristos
796f11bdf1Schristos /*
806f11bdf1Schristos * authentication management
816f11bdf1Schristos */
826f11bdf1Schristos PAM_EXTERN int
83bb62ec41Schristos /*ARGSUSED*/
pam_sm_authenticate(pam_handle_t * pamh,int flags __unused,int argc __unused,const char * argv[]__unused)846f11bdf1Schristos pam_sm_authenticate(pam_handle_t *pamh, int flags __unused,
856f11bdf1Schristos int argc __unused, const char *argv[] __unused)
866f11bdf1Schristos {
876f11bdf1Schristos login_cap_t *lc;
8859cbc9e2Sthorpej struct passwd *pwd, pwres;
896f11bdf1Schristos int retval;
90bb62ec41Schristos const char *pass, *user, *realpw;
9159cbc9e2Sthorpej char pwbuf[1024];
926f11bdf1Schristos
9301cf9d02Slukem pwd = NULL;
946f11bdf1Schristos if (openpam_get_option(pamh, PAM_OPT_AUTH_AS_SELF)) {
9559cbc9e2Sthorpej (void) getpwnam_r(getlogin(), &pwres, pwbuf, sizeof(pwbuf),
9659cbc9e2Sthorpej &pwd);
976f11bdf1Schristos } else {
986f11bdf1Schristos retval = pam_get_user(pamh, &user, NULL);
996f11bdf1Schristos if (retval != PAM_SUCCESS)
1006f11bdf1Schristos return (retval);
101bb62ec41Schristos PAM_LOG("Got user: %s", user);
10259cbc9e2Sthorpej (void) getpwnam_r(user, &pwres, pwbuf, sizeof(pwbuf), &pwd);
1036f11bdf1Schristos }
1046f11bdf1Schristos
1056f11bdf1Schristos if (pwd != NULL) {
1066f11bdf1Schristos PAM_LOG("Doing real authentication");
1076f11bdf1Schristos realpw = pwd->pw_passwd;
1086f11bdf1Schristos if (realpw[0] == '\0') {
1096f11bdf1Schristos if (!(flags & PAM_DISALLOW_NULL_AUTHTOK) &&
1106f11bdf1Schristos openpam_get_option(pamh, PAM_OPT_NULLOK))
1116f11bdf1Schristos return (PAM_SUCCESS);
1126f11bdf1Schristos realpw = "*";
1136f11bdf1Schristos }
1146f11bdf1Schristos } else {
1156f11bdf1Schristos PAM_LOG("Doing dummy authentication");
1166f11bdf1Schristos realpw = "*";
1176f11bdf1Schristos }
118fd65ca01Schristos lc = login_getpwclass(pwd);
119bb62ec41Schristos retval = pam_get_authtok(pamh, PAM_AUTHTOK, &pass, NULL);
1206f11bdf1Schristos login_close(lc);
1216f11bdf1Schristos if (retval != PAM_SUCCESS)
1226f11bdf1Schristos return (retval);
1236f11bdf1Schristos PAM_LOG("Got password");
1246f11bdf1Schristos if (strcmp(crypt(pass, realpw), realpw) == 0)
1256f11bdf1Schristos return (PAM_SUCCESS);
1266f11bdf1Schristos
1276f11bdf1Schristos PAM_VERBOSE_ERROR("UNIX authentication refused");
1286f11bdf1Schristos return (PAM_AUTH_ERR);
1296f11bdf1Schristos }
1306f11bdf1Schristos
1316f11bdf1Schristos PAM_EXTERN int
132bb62ec41Schristos /*ARGSUSED*/
pam_sm_setcred(pam_handle_t * pamh __unused,int flags __unused,int argc __unused,const char * argv[]__unused)1336f11bdf1Schristos pam_sm_setcred(pam_handle_t *pamh __unused, int flags __unused,
1346f11bdf1Schristos int argc __unused, const char *argv[] __unused)
1356f11bdf1Schristos {
1366f11bdf1Schristos
1376f11bdf1Schristos return (PAM_SUCCESS);
1386f11bdf1Schristos }
1396f11bdf1Schristos
1406f11bdf1Schristos /*
1416f11bdf1Schristos * account management
1426f11bdf1Schristos */
1436f11bdf1Schristos PAM_EXTERN int
144bb62ec41Schristos /*ARGSUSED*/
pam_sm_acct_mgmt(pam_handle_t * pamh,int flags __unused,int argc __unused,const char * argv[]__unused)1456f11bdf1Schristos pam_sm_acct_mgmt(pam_handle_t *pamh, int flags __unused,
1466f11bdf1Schristos int argc __unused, const char *argv[] __unused)
1476f11bdf1Schristos {
14859cbc9e2Sthorpej struct passwd *pwd, pwres;
149bb62ec41Schristos struct timeval now;
1506f11bdf1Schristos login_cap_t *lc;
1516f11bdf1Schristos time_t warntime;
1526f11bdf1Schristos int retval;
1536f11bdf1Schristos const char *user;
15459cbc9e2Sthorpej char pwbuf[1024];
1556f11bdf1Schristos
1566f11bdf1Schristos retval = pam_get_user(pamh, &user, NULL);
1576f11bdf1Schristos if (retval != PAM_SUCCESS)
1586f11bdf1Schristos return (retval);
1596f11bdf1Schristos
16059cbc9e2Sthorpej if (user == NULL ||
1612a62e4e1Schristos getpwnam_r(user, &pwres, pwbuf, sizeof(pwbuf), &pwd) != 0 ||
1622a62e4e1Schristos pwd == NULL)
1636f11bdf1Schristos return (PAM_SERVICE_ERR);
1646f11bdf1Schristos
1656f11bdf1Schristos PAM_LOG("Got user: %s", user);
1666f11bdf1Schristos
1676f11bdf1Schristos if (*pwd->pw_passwd == '\0' &&
1686f11bdf1Schristos (flags & PAM_DISALLOW_NULL_AUTHTOK) != 0)
1696f11bdf1Schristos return (PAM_NEW_AUTHTOK_REQD);
1706f11bdf1Schristos
1716f11bdf1Schristos lc = login_getpwclass(pwd);
1726f11bdf1Schristos if (lc == NULL) {
1736f11bdf1Schristos PAM_LOG("Unable to get login class for user %s", user);
1746f11bdf1Schristos return (PAM_SERVICE_ERR);
1756f11bdf1Schristos }
1766f11bdf1Schristos
1776f11bdf1Schristos PAM_LOG("Got login_cap");
1786f11bdf1Schristos
1796f11bdf1Schristos if (pwd->pw_change || pwd->pw_expire)
180bb62ec41Schristos (void) gettimeofday(&now, NULL);
181bb62ec41Schristos
182bb62ec41Schristos warntime = (time_t)login_getcaptime(lc, "password-warn",
183bb62ec41Schristos (quad_t)(_PASSWORD_WARNDAYS * SECSPERDAY),
184bb62ec41Schristos (quad_t)(_PASSWORD_WARNDAYS * SECSPERDAY));
1856f11bdf1Schristos
1866f11bdf1Schristos /*
1876f11bdf1Schristos * Check pw_expire before pw_change - no point in letting the
1886f11bdf1Schristos * user change the password on an expired account.
1896f11bdf1Schristos */
1906f11bdf1Schristos
1916f11bdf1Schristos if (pwd->pw_expire) {
192bb62ec41Schristos if (now.tv_sec >= pwd->pw_expire) {
1936f11bdf1Schristos login_close(lc);
1946f11bdf1Schristos return (PAM_ACCT_EXPIRED);
195bb62ec41Schristos } else if (pwd->pw_expire - now.tv_sec < warntime &&
1966f11bdf1Schristos (flags & PAM_SILENT) == 0) {
1976f11bdf1Schristos pam_error(pamh, "Warning: your account expires on %s",
1986f11bdf1Schristos ctime(&pwd->pw_expire));
1996f11bdf1Schristos }
2006f11bdf1Schristos }
2016f11bdf1Schristos
2026f11bdf1Schristos if (pwd->pw_change) {
203bb62ec41Schristos /* XXX How to handle _PASSWORD_CHGNOW? --thorpej */
204bb62ec41Schristos if (now.tv_sec >= pwd->pw_change) {
205bb62ec41Schristos login_close(lc);
206bb62ec41Schristos return (PAM_NEW_AUTHTOK_REQD);
207bb62ec41Schristos } else if (pwd->pw_change - now.tv_sec < warntime &&
2086f11bdf1Schristos (flags & PAM_SILENT) == 0) {
2096f11bdf1Schristos pam_error(pamh, "Warning: your password expires on %s",
2106f11bdf1Schristos ctime(&pwd->pw_change));
2116f11bdf1Schristos }
2126f11bdf1Schristos }
2136f11bdf1Schristos
2146f11bdf1Schristos login_close(lc);
2156f11bdf1Schristos
216bb62ec41Schristos return (PAM_SUCCESS);
217bb62ec41Schristos }
218bb62ec41Schristos
219bb62ec41Schristos #ifdef YP
220bb62ec41Schristos /*
221bb62ec41Schristos * yp_check_user:
222bb62ec41Schristos *
223bb62ec41Schristos * Helper function; check that a user exists in the NIS
224bb62ec41Schristos * password map.
225bb62ec41Schristos */
226bb62ec41Schristos static int
yp_check_user(const char * domain,const char * user)227bb62ec41Schristos yp_check_user(const char *domain, const char *user)
228bb62ec41Schristos {
229bb62ec41Schristos char *val;
230bb62ec41Schristos int reason, vallen;
231bb62ec41Schristos
232bb62ec41Schristos val = NULL;
233bb62ec41Schristos reason = yp_match(domain, "passwd.byname", user, (int)strlen(user),
234bb62ec41Schristos &val, &vallen);
235bb62ec41Schristos if (reason != 0) {
236bb62ec41Schristos if (val != NULL)
237bb62ec41Schristos free(val);
238bb62ec41Schristos return (0);
239bb62ec41Schristos }
240bb62ec41Schristos free(val);
241bb62ec41Schristos return (1);
242bb62ec41Schristos }
243bb62ec41Schristos
244bb62ec41Schristos static int
245bb62ec41Schristos /*ARGSUSED*/
yp_set_password(pam_handle_t * pamh,struct passwd * opwd,struct passwd * pwd,const char * old_pass,const char * domain)246bb62ec41Schristos yp_set_password(pam_handle_t *pamh, struct passwd *opwd,
247bb62ec41Schristos struct passwd *pwd, const char *old_pass, const char *domain)
248bb62ec41Schristos {
249bb62ec41Schristos char *master;
250bb62ec41Schristos int r, rpcport, status;
251ed9315c5Sjoerg enum clnt_stat r2;
252bb62ec41Schristos struct yppasswd yppwd;
253bb62ec41Schristos CLIENT *client;
254bb62ec41Schristos uid_t uid;
255bb62ec41Schristos int retval = PAM_SERVICE_ERR;
256bb62ec41Schristos struct timeval tv;
257bb62ec41Schristos
258bb62ec41Schristos /*
259bb62ec41Schristos * Find the master for the passwd map; it should be running
260bb62ec41Schristos * rpc.yppasswdd.
261bb62ec41Schristos */
262bb62ec41Schristos if ((r = yp_master(domain, "passwd.byname", &master)) != 0) {
263bb62ec41Schristos pam_error(pamh, "Can't find master NIS server. Reason: %s",
264bb62ec41Schristos yperr_string(r));
265bb62ec41Schristos return (PAM_SERVICE_ERR);
266bb62ec41Schristos }
267bb62ec41Schristos
268bb62ec41Schristos /*
269bb62ec41Schristos * Ask the portmapper for the port of rpc.yppasswdd.
270bb62ec41Schristos */
271bb62ec41Schristos if ((rpcport = getrpcport(master, YPPASSWDPROG,
272bb62ec41Schristos YPPASSWDPROC_UPDATE, IPPROTO_UDP)) == 0) {
273bb62ec41Schristos pam_error(pamh,
27475d2abaeSandvar "Master NIS server not running yppasswd daemon.\n\t"
275bb62ec41Schristos "Can't change NIS password.");
276bb62ec41Schristos return (PAM_SERVICE_ERR);
277bb62ec41Schristos }
278bb62ec41Schristos
279bb62ec41Schristos /*
280bb62ec41Schristos * Be sure the port is privileged.
281bb62ec41Schristos */
282bb62ec41Schristos if (rpcport >= IPPORT_RESERVED) {
283bb62ec41Schristos pam_error(pamh, "yppasswd daemon is on an invalid port.");
284bb62ec41Schristos return (PAM_SERVICE_ERR);
285bb62ec41Schristos }
286bb62ec41Schristos
287bb62ec41Schristos uid = getuid();
288bb62ec41Schristos if (uid != 0 && uid != pwd->pw_uid) {
289bb62ec41Schristos pam_error(pamh, "You may only change your own password: %s",
290bb62ec41Schristos strerror(EACCES));
291bb62ec41Schristos return (PAM_SERVICE_ERR);
292bb62ec41Schristos }
293bb62ec41Schristos
294bb62ec41Schristos /*
295bb62ec41Schristos * Fill in the yppasswd structure for yppasswdd.
296bb62ec41Schristos */
297bb62ec41Schristos memset(&yppwd, 0, sizeof(yppwd));
2980a40f744Sthorpej yppwd.oldpass = strdup(old_pass);
299bb62ec41Schristos if ((yppwd.newpw.pw_passwd = strdup(pwd->pw_passwd)) == NULL)
300bb62ec41Schristos goto malloc_failure;
301bb62ec41Schristos if ((yppwd.newpw.pw_name = strdup(pwd->pw_name)) == NULL)
302bb62ec41Schristos goto malloc_failure;
3039778b180Schristos yppwd.newpw.pw_uid = (int)pwd->pw_uid;
3049778b180Schristos yppwd.newpw.pw_gid = (int)pwd->pw_gid;
305bb62ec41Schristos if ((yppwd.newpw.pw_gecos = strdup(pwd->pw_gecos)) == NULL)
306bb62ec41Schristos goto malloc_failure;
307bb62ec41Schristos if ((yppwd.newpw.pw_dir = strdup(pwd->pw_dir)) == NULL)
308bb62ec41Schristos goto malloc_failure;
309bb62ec41Schristos if ((yppwd.newpw.pw_shell = strdup(pwd->pw_shell)) == NULL)
310bb62ec41Schristos goto malloc_failure;
311bb62ec41Schristos
312bb62ec41Schristos client = clnt_create(master, YPPASSWDPROG, YPPASSWDVERS, "udp");
313bb62ec41Schristos if (client == NULL) {
314bb62ec41Schristos pam_error(pamh, "Can't contact yppasswdd on %s: Reason: %s",
315bb62ec41Schristos master, yperr_string(YPERR_YPBIND));
316bb62ec41Schristos goto out;
317bb62ec41Schristos }
318bb62ec41Schristos
319bb62ec41Schristos client->cl_auth = authunix_create_default();
320bb62ec41Schristos tv.tv_sec = 2;
321bb62ec41Schristos tv.tv_usec = 0;
322ed9315c5Sjoerg r2 = clnt_call(client, YPPASSWDPROC_UPDATE,
323bb62ec41Schristos xdr_yppasswd, &yppwd, xdr_int, &status, tv);
324ed9315c5Sjoerg if (r2 != RPC_SUCCESS)
325bb62ec41Schristos pam_error(pamh, "RPC to yppasswdd failed.");
326bb62ec41Schristos else if (status)
327bb62ec41Schristos pam_error(pamh, "Couldn't change NIS password.");
328bb62ec41Schristos else {
329bb62ec41Schristos pam_info(pamh, "The NIS password has been changed on %s, "
330bb62ec41Schristos "the master NIS passwd server.", master);
331bb62ec41Schristos retval = PAM_SUCCESS;
332bb62ec41Schristos }
333bb62ec41Schristos
334bb62ec41Schristos out:
3350a40f744Sthorpej if (yppwd.oldpass != NULL)
3360a40f744Sthorpej free(yppwd.oldpass);
337bb62ec41Schristos if (yppwd.newpw.pw_passwd != NULL)
338bb62ec41Schristos free(yppwd.newpw.pw_passwd);
339bb62ec41Schristos if (yppwd.newpw.pw_name != NULL)
340bb62ec41Schristos free(yppwd.newpw.pw_name);
341bb62ec41Schristos if (yppwd.newpw.pw_gecos != NULL)
342bb62ec41Schristos free(yppwd.newpw.pw_gecos);
343bb62ec41Schristos if (yppwd.newpw.pw_dir != NULL)
344bb62ec41Schristos free(yppwd.newpw.pw_dir);
345bb62ec41Schristos if (yppwd.newpw.pw_shell != NULL)
346bb62ec41Schristos free(yppwd.newpw.pw_shell);
3476f11bdf1Schristos return (retval);
348bb62ec41Schristos
349bb62ec41Schristos malloc_failure:
350bb62ec41Schristos pam_error(pamh, "memory allocation failure");
351bb62ec41Schristos goto out;
352bb62ec41Schristos }
353bb62ec41Schristos #endif /* YP */
354bb62ec41Schristos
355bb62ec41Schristos static int
local_set_password(pam_handle_t * pamh,struct passwd * opwd,struct passwd * pwd)356bb62ec41Schristos local_set_password(pam_handle_t *pamh, struct passwd *opwd,
357bb62ec41Schristos struct passwd *pwd)
358bb62ec41Schristos {
359bb62ec41Schristos char errbuf[200];
360bb62ec41Schristos int tfd, pfd;
361bb62ec41Schristos
362bb62ec41Schristos pw_init();
363bb62ec41Schristos tfd = pw_lock(0);
364bb62ec41Schristos if (tfd < 0) {
365bb62ec41Schristos pam_error(pamh, "The password file is busy, waiting...");
366bb62ec41Schristos tfd = pw_lock(10);
367bb62ec41Schristos if (tfd < 0) {
368bb62ec41Schristos pam_error(pamh, "The password file is still busy, "
369bb62ec41Schristos "try again later.");
370bb62ec41Schristos return (PAM_SERVICE_ERR);
371bb62ec41Schristos }
372bb62ec41Schristos }
373bb62ec41Schristos
374bb62ec41Schristos pfd = open(_PATH_MASTERPASSWD, O_RDONLY, 0);
375bb62ec41Schristos if (pfd < 0) {
376bb62ec41Schristos pam_error(pamh, "%s: %s", _PATH_MASTERPASSWD, strerror(errno));
377bb62ec41Schristos pw_abort();
378bb62ec41Schristos return (PAM_SERVICE_ERR);
379bb62ec41Schristos }
380bb62ec41Schristos
381bb62ec41Schristos if (pw_copyx(pfd, tfd, pwd, opwd, errbuf, sizeof(errbuf)) == 0) {
382bb62ec41Schristos pam_error(pamh, "Unable to update password entry: %s",
383bb62ec41Schristos errbuf);
384bb62ec41Schristos pw_abort();
385bb62ec41Schristos return (PAM_SERVICE_ERR);
386bb62ec41Schristos }
387bb62ec41Schristos
388bb62ec41Schristos if (pw_mkdb(pwd->pw_name, opwd->pw_change == pwd->pw_change) < 0) {
389bb62ec41Schristos pam_error(pamh, "Unable to rebuild local password database.");
390bb62ec41Schristos pw_abort();
391bb62ec41Schristos return (PAM_SERVICE_ERR);
392bb62ec41Schristos }
393bb62ec41Schristos
394bb62ec41Schristos return (PAM_SUCCESS);
3956f11bdf1Schristos }
3966f11bdf1Schristos
3976f11bdf1Schristos /*
3986f11bdf1Schristos * password management
3996f11bdf1Schristos *
4006f11bdf1Schristos * standard Unix and NIS password changing
4016f11bdf1Schristos */
4026f11bdf1Schristos PAM_EXTERN int
403bb62ec41Schristos /*ARGSUSED*/
pam_sm_chauthtok(pam_handle_t * pamh,int flags,int argc __unused,const char * argv[]__unused)4046f11bdf1Schristos pam_sm_chauthtok(pam_handle_t *pamh, int flags,
4056f11bdf1Schristos int argc __unused, const char *argv[] __unused)
4066f11bdf1Schristos {
40774cf7887Sjnemeth struct passwd *pwd, new_pwd, old_pwd;
4086f11bdf1Schristos login_cap_t *lc;
409bb62ec41Schristos const char *user, *passwd_db, *new_pass, *old_pass, *p;
410bb62ec41Schristos int retval, tries, min_pw_len = 0, pw_expiry = 0;
411bb62ec41Schristos char salt[_PASSWORD_LEN+1];
41259cbc9e2Sthorpej char old_pwbuf[1024];
413bb62ec41Schristos #ifdef YP
414bb62ec41Schristos char *domain;
415bb62ec41Schristos int r;
416bb62ec41Schristos #endif
4176f11bdf1Schristos
41801cf9d02Slukem pwd = NULL;
419f3587062Sjnemeth if (openpam_get_option(pamh, PAM_OPT_AUTH_AS_SELF)) {
420f3587062Sjnemeth if ((user = getlogin()) == NULL) {
421f3587062Sjnemeth pam_error(pamh, "Unable to determine user.");
422f3587062Sjnemeth return (PAM_SERVICE_ERR);
423f3587062Sjnemeth }
424f3587062Sjnemeth (void) getpwnam_r(user, &old_pwd, old_pwbuf,
42559cbc9e2Sthorpej sizeof(old_pwbuf), &pwd);
426f3587062Sjnemeth } else {
4276f11bdf1Schristos retval = pam_get_user(pamh, &user, NULL);
4286f11bdf1Schristos if (retval != PAM_SUCCESS)
4296f11bdf1Schristos return (retval);
43059cbc9e2Sthorpej (void) getpwnam_r(user, &old_pwd, old_pwbuf,
43159cbc9e2Sthorpej sizeof(old_pwbuf), &pwd);
4326f11bdf1Schristos }
4336f11bdf1Schristos
4346f11bdf1Schristos if (pwd == NULL)
4356f11bdf1Schristos return (PAM_AUTHTOK_RECOVERY_ERR);
4366f11bdf1Schristos
4376f11bdf1Schristos PAM_LOG("Got user: %s", user);
4386f11bdf1Schristos
439bb62ec41Schristos /*
440bb62ec41Schristos * Determine which password type we're going to change, and
441bb62ec41Schristos * remember it.
442bb62ec41Schristos *
443bb62ec41Schristos * NOTE: domain does not need to be freed; its storage is
444bb62ec41Schristos * allocated statically in libc.
445bb62ec41Schristos */
446bb62ec41Schristos passwd_db = openpam_get_option(pamh, "passwd_db");
447bb62ec41Schristos if (passwd_db == NULL) {
4486f11bdf1Schristos #ifdef YP
449bb62ec41Schristos /* Prefer YP, if configured. */
450bb62ec41Schristos if (_yp_check(NULL)) {
451bb62ec41Schristos /* If _yp_check() succeeded, then this must. */
452bb62ec41Schristos if ((r = yp_get_default_domain(&domain)) != 0) {
453bb62ec41Schristos pam_error(pamh,
454bb62ec41Schristos "Unable to get NIS domain, reason: %s",
455bb62ec41Schristos yperr_string(r));
4566f11bdf1Schristos return (PAM_SERVICE_ERR);
4576f11bdf1Schristos }
458bb62ec41Schristos if (yp_check_user(domain, user))
459bb62ec41Schristos passwd_db = "nis";
4606f11bdf1Schristos }
4616f11bdf1Schristos #endif
462bb62ec41Schristos /* Otherwise we always use local files. */
463bb62ec41Schristos if (passwd_db == NULL) {
464bb62ec41Schristos /* XXX Any validation to do here? */
465bb62ec41Schristos passwd_db = "files";
466bb62ec41Schristos }
467bb62ec41Schristos
468f3587062Sjnemeth if ((retval = openpam_set_option(pamh, "passwd_db",
469bb62ec41Schristos passwd_db)) != PAM_SUCCESS) {
4706f11bdf1Schristos return (retval);
4716f11bdf1Schristos }
472bb62ec41Schristos } else {
473bb62ec41Schristos /* Check to see if the specified password DB is usable. */
474bb62ec41Schristos #ifdef YP
475bb62ec41Schristos if (strcmp(passwd_db, "nis") == 0) {
476bb62ec41Schristos if (_yp_check(NULL) == 0) {
477bb62ec41Schristos pam_error(pamh, "NIS not in use.");
478bb62ec41Schristos return (PAM_SERVICE_ERR);
4796f11bdf1Schristos }
480bb62ec41Schristos if ((r = yp_get_default_domain(&domain)) != 0) {
481bb62ec41Schristos pam_error(pamh,
482bb62ec41Schristos "Unable to get NIS domain, reason: %s",
483bb62ec41Schristos yperr_string(r));
484bb62ec41Schristos return (PAM_SERVICE_ERR);
485bb62ec41Schristos }
486bb62ec41Schristos if (yp_check_user(domain, user) == 0) {
487bb62ec41Schristos pam_error(pamh,
488bb62ec41Schristos "User %s does not exist in NIS.", user);
489bb62ec41Schristos return (PAM_USER_UNKNOWN);
490bb62ec41Schristos }
491bb62ec41Schristos goto known_passwd_db;
492bb62ec41Schristos }
493bb62ec41Schristos #endif
494bb62ec41Schristos if (strcmp(passwd_db, "files") == 0) {
495bb62ec41Schristos /* XXX Any validation to do here? */
496bb62ec41Schristos goto known_passwd_db;
497bb62ec41Schristos }
498bb62ec41Schristos pam_error(pamh, "Unknown Unix password DB: %s", passwd_db);
499bb62ec41Schristos return (PAM_SERVICE_ERR);
500bb62ec41Schristos }
501bb62ec41Schristos known_passwd_db:
502bb62ec41Schristos
503bb62ec41Schristos if (flags & PAM_PRELIM_CHECK) {
504bb62ec41Schristos PAM_LOG("PRELIM round");
505bb62ec41Schristos
506bb62ec41Schristos if (strcmp(passwd_db, "files") == 0) {
507bb62ec41Schristos if (getuid() == 0) {
508bb62ec41Schristos /* Root doesn't need the old password. */
509bb62ec41Schristos return (pam_set_item(pamh, PAM_OLDAUTHTOK, ""));
510bb62ec41Schristos }
511c08078c2Stonnerre /*
512c08078c2Stonnerre * Apparently we're not root, so let's forbid editing
513c08078c2Stonnerre * root.
514c08078c2Stonnerre * XXX Check for some flag to indicate if this
515c08078c2Stonnerre * XXX is the desired behavior.
516c08078c2Stonnerre */
517c08078c2Stonnerre if (pwd->pw_uid == 0)
518c08078c2Stonnerre return (PAM_PERM_DENIED);
519bb62ec41Schristos }
520bb62ec41Schristos
521bb62ec41Schristos if (pwd->pw_passwd[0] == '\0') {
522bb62ec41Schristos /*
523bb62ec41Schristos * No password case.
524*730fdfa6Sandvar * XXX Are we giving too much away by not prompting
525bb62ec41Schristos * XXX for a password?
526bb62ec41Schristos * XXX Check PAM_DISALLOW_NULL_AUTHTOK
527bb62ec41Schristos */
528bb62ec41Schristos return (pam_set_item(pamh, PAM_OLDAUTHTOK, ""));
529bb62ec41Schristos } else {
530bb62ec41Schristos retval = pam_get_authtok(pamh, PAM_OLDAUTHTOK,
531bb62ec41Schristos &old_pass, NULL);
532bb62ec41Schristos if (retval != PAM_SUCCESS)
533bb62ec41Schristos return (retval);
534bb62ec41Schristos if (strcmp(crypt(old_pass, pwd->pw_passwd),
535bb62ec41Schristos pwd->pw_passwd) != 0)
536bb62ec41Schristos return (PAM_PERM_DENIED);
537bb62ec41Schristos return (PAM_SUCCESS);
538bb62ec41Schristos }
539bb62ec41Schristos }
540bb62ec41Schristos
541bb62ec41Schristos if (flags & PAM_UPDATE_AUTHTOK) {
542c9cb0c3bSchristos char option[LINE_MAX], *key, *opt;
543c9cb0c3bSchristos
5446f11bdf1Schristos PAM_LOG("UPDATE round");
5456f11bdf1Schristos
546fd65ca01Schristos if ((lc = login_getpwclass(pwd)) != NULL) {
547bb62ec41Schristos min_pw_len = (int) login_getcapnum(lc,
548bb62ec41Schristos "minpasswordlen", (quad_t)0, (quad_t)0);
549bb62ec41Schristos pw_expiry = (int) login_getcapnum(lc,
550bb62ec41Schristos "passwordtime", (quad_t)0, (quad_t)0);
551bb62ec41Schristos login_close(lc);
552bb62ec41Schristos }
553bb62ec41Schristos
554bb62ec41Schristos retval = pam_get_authtok(pamh, PAM_OLDAUTHTOK, &old_pass, NULL);
5556f11bdf1Schristos if (retval != PAM_SUCCESS)
5566f11bdf1Schristos return (retval);
5576f11bdf1Schristos
558bb62ec41Schristos /* Get the new password. */
559bb62ec41Schristos for (tries = 0;;) {
560bb62ec41Schristos retval = pam_get_authtok(pamh, PAM_AUTHTOK, &new_pass,
561bb62ec41Schristos NULL);
562bb62ec41Schristos if (retval == PAM_TRY_AGAIN) {
563bb62ec41Schristos pam_error(pamh,
564bb62ec41Schristos "Mismatch; try again, EOF to quit.");
565bb62ec41Schristos continue;
5666f11bdf1Schristos }
5676f11bdf1Schristos if (retval != PAM_SUCCESS) {
5686f11bdf1Schristos PAM_VERBOSE_ERROR("Unable to get new password");
5696f11bdf1Schristos return (retval);
5706f11bdf1Schristos }
571bb62ec41Schristos /* Successfully got new password. */
572bb62ec41Schristos if (new_pass[0] == '\0') {
573bb62ec41Schristos pam_info(pamh, "Password unchanged.");
574bb62ec41Schristos return (PAM_SUCCESS);
575bb62ec41Schristos }
576ee7c6ab3Slukem if (min_pw_len > 0 && strlen(new_pass) < (size_t)min_pw_len) {
577bb62ec41Schristos pam_error(pamh, "Password is too short.");
578fa423103Sdrochner goto retry;
579bb62ec41Schristos }
580bb62ec41Schristos if (strlen(new_pass) <= 5 && ++tries < 2) {
581bb62ec41Schristos pam_error(pamh,
582bb62ec41Schristos "Please enter a longer password.");
583fa423103Sdrochner goto retry;
584bb62ec41Schristos }
585bb62ec41Schristos for (p = new_pass; *p && islower((unsigned char)*p); ++p);
586bb62ec41Schristos if (!*p && ++tries < 2) {
587bb62ec41Schristos pam_error(pamh,
588bb62ec41Schristos "Please don't use an all-lower case "
589bb62ec41Schristos "password.\nUnusual capitalization, "
590bb62ec41Schristos "control characters or digits are "
591bb62ec41Schristos "suggested.");
592fa423103Sdrochner goto retry;
593bb62ec41Schristos }
594bb62ec41Schristos /* Password is OK. */
595bb62ec41Schristos break;
596fa423103Sdrochner retry:
597fa423103Sdrochner pam_set_item(pamh, PAM_AUTHTOK, NULL);
598bb62ec41Schristos }
599c9cb0c3bSchristos pw_getpwconf(option, sizeof(option), pwd,
600bb62ec41Schristos #ifdef YP
601c9cb0c3bSchristos strcmp(passwd_db, "nis") == 0 ? "ypcipher" :
602bb62ec41Schristos #endif
603c9cb0c3bSchristos "localcipher");
604c9cb0c3bSchristos opt = option;
605c9cb0c3bSchristos key = strsep(&opt, ",");
606c9cb0c3bSchristos
607c9cb0c3bSchristos if (pw_gensalt(salt, _PASSWORD_LEN, key, opt) == -1) {
608bb62ec41Schristos pam_error(pamh, "Couldn't generate salt.");
609bb62ec41Schristos return (PAM_SERVICE_ERR);
610bb62ec41Schristos }
6116f11bdf1Schristos
61274cf7887Sjnemeth new_pwd = old_pwd;
61374cf7887Sjnemeth pwd = &new_pwd;
6146f11bdf1Schristos pwd->pw_passwd = crypt(new_pass, salt);
615bb62ec41Schristos pwd->pw_change = pw_expiry ? pw_expiry + time(NULL) : 0;
6166f11bdf1Schristos
617bb62ec41Schristos retval = PAM_SERVICE_ERR;
618bb62ec41Schristos if (strcmp(passwd_db, "files") == 0)
619bb62ec41Schristos retval = local_set_password(pamh, &old_pwd, pwd);
620bb62ec41Schristos #ifdef YP
621bb62ec41Schristos if (strcmp(passwd_db, "nis") == 0)
622bb62ec41Schristos retval = yp_set_password(pamh, &old_pwd, pwd, old_pass,
623bb62ec41Schristos domain);
624bb62ec41Schristos #endif
6256f11bdf1Schristos return (retval);
6266f11bdf1Schristos }
6276f11bdf1Schristos
628bb62ec41Schristos PAM_LOG("Illegal flags argument");
629bb62ec41Schristos return (PAM_ABORT);
630bb62ec41Schristos }
6316f11bdf1Schristos
632bb62ec41Schristos PAM_MODULE_ENTRY("pam_unix");
633