]> CyberLeo.Net >> Repos - FreeBSD/FreeBSD.git/blob - lib/libpam/modules/pam_unix/pam_unix.c
Update to bmake-20200704
[FreeBSD/FreeBSD.git] / lib / libpam / modules / pam_unix / pam_unix.c
1 /*-
2  * SPDX-License-Identifier: BSD-3-Clause
3  *
4  * Copyright 1998 Juniper Networks, Inc.
5  * All rights reserved.
6  * Copyright (c) 2002-2003 Networks Associates Technology, Inc.
7  * All rights reserved.
8  *
9  * Portions of this software was developed for the FreeBSD Project by
10  * ThinkSec AS and NAI Labs, the Security Research Division of Network
11  * Associates, Inc.  under DARPA/SPAWAR contract N66001-01-C-8035
12  * ("CBOSS"), as part of the DARPA CHATS research program.
13  *
14  * Redistribution and use in source and binary forms, with or without
15  * modification, are permitted provided that the following conditions
16  * are met:
17  * 1. Redistributions of source code must retain the above copyright
18  *    notice, this list of conditions and the following disclaimer.
19  * 2. Redistributions in binary form must reproduce the above copyright
20  *    notice, this list of conditions and the following disclaimer in the
21  *    documentation and/or other materials provided with the distribution.
22  * 3. The name of the author may not be used to endorse or promote
23  *    products derived from this software without specific prior written
24  *    permission.
25  *
26  * THIS SOFTWARE IS PROVIDED BY THE AUTHOR AND CONTRIBUTORS ``AS IS'' AND
27  * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
28  * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
29  * ARE DISCLAIMED.  IN NO EVENT SHALL THE AUTHOR OR CONTRIBUTORS BE LIABLE
30  * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
31  * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS
32  * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION)
33  * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT
34  * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY
35  * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF
36  * SUCH DAMAGE.
37  */
38
39 #include <sys/cdefs.h>
40 __FBSDID("$FreeBSD$");
41
42 #include <sys/param.h>
43 #include <sys/socket.h>
44 #include <sys/time.h>
45 #include <netinet/in.h>
46 #include <arpa/inet.h>
47
48 #include <login_cap.h>
49 #include <netdb.h>
50 #include <pwd.h>
51 #include <stdlib.h>
52 #include <string.h>
53 #include <stdio.h>
54 #include <syslog.h>
55 #include <time.h>
56 #include <unistd.h>
57
58 #include <libutil.h>
59
60 #ifdef YP
61 #include <ypclnt.h>
62 #endif
63
64 #define PAM_SM_AUTH
65 #define PAM_SM_ACCOUNT
66 #define PAM_SM_PASSWORD
67
68 #include <security/pam_appl.h>
69 #include <security/pam_modules.h>
70 #include <security/pam_mod_misc.h>
71
72 #define PASSWORD_HASH           "md5"
73 #define DEFAULT_WARN            (2L * 7L * 86400L)  /* Two weeks */
74 #define SALTSIZE                32
75
76 #define LOCKED_PREFIX           "*LOCKED*"
77 #define LOCKED_PREFIX_LEN       (sizeof(LOCKED_PREFIX) - 1)
78
79 static void makesalt(char []);
80
81 static char password_hash[] =           PASSWORD_HASH;
82
83 #define PAM_OPT_LOCAL_PASS      "local_pass"
84 #define PAM_OPT_NIS_PASS        "nis_pass"
85
86 /*
87  * authentication management
88  */
89 PAM_EXTERN int
90 pam_sm_authenticate(pam_handle_t *pamh, int flags __unused,
91     int argc __unused, const char *argv[] __unused)
92 {
93         login_cap_t *lc;
94         struct passwd *pwd;
95         int retval;
96         const char *pass, *user, *realpw, *prompt;
97
98         if (openpam_get_option(pamh, PAM_OPT_AUTH_AS_SELF)) {
99                 user = getlogin();
100         } else {
101                 retval = pam_get_user(pamh, &user, NULL);
102                 if (retval != PAM_SUCCESS)
103                         return (retval);
104         }
105         pwd = getpwnam(user);
106
107         PAM_LOG("Got user: %s", user);
108
109         if (pwd != NULL) {
110                 PAM_LOG("Doing real authentication");
111                 realpw = pwd->pw_passwd;
112                 if (realpw[0] == '\0') {
113                         if (!(flags & PAM_DISALLOW_NULL_AUTHTOK) &&
114                             openpam_get_option(pamh, PAM_OPT_NULLOK))
115                                 return (PAM_SUCCESS);
116                         PAM_LOG("Password is empty, using fake password");
117                         realpw = "*";
118                 }
119                 lc = login_getpwclass(pwd);
120         } else {
121                 PAM_LOG("Doing dummy authentication");
122                 realpw = "*";
123                 lc = login_getclass(NULL);
124         }
125         prompt = login_getcapstr(lc, "passwd_prompt", NULL, NULL);
126         retval = pam_get_authtok(pamh, PAM_AUTHTOK, &pass, prompt);
127         login_close(lc);
128         if (retval != PAM_SUCCESS)
129                 return (retval);
130         PAM_LOG("Got password");
131         if (strnlen(pass, _PASSWORD_LEN + 1) > _PASSWORD_LEN) {
132                 PAM_LOG("Password is too long, using fake password");
133                 realpw = "*";
134         }
135         if (strcmp(crypt(pass, realpw), realpw) == 0)
136                 return (PAM_SUCCESS);
137
138         PAM_VERBOSE_ERROR("UNIX authentication refused");
139         return (PAM_AUTH_ERR);
140 }
141
142 PAM_EXTERN int
143 pam_sm_setcred(pam_handle_t *pamh __unused, int flags __unused,
144     int argc __unused, const char *argv[] __unused)
145 {
146
147         return (PAM_SUCCESS);
148 }
149
150 /*
151  * account management
152  */
153 PAM_EXTERN int
154 pam_sm_acct_mgmt(pam_handle_t *pamh, int flags __unused,
155     int argc __unused, const char *argv[] __unused)
156 {
157         struct addrinfo hints, *res;
158         struct passwd *pwd;
159         struct timeval tp;
160         login_cap_t *lc;
161         time_t warntime;
162         int retval;
163         const char *user;
164         const void *rhost, *tty;
165         char rhostip[MAXHOSTNAMELEN] = "";
166
167         retval = pam_get_user(pamh, &user, NULL);
168         if (retval != PAM_SUCCESS)
169                 return (retval);
170
171         if (user == NULL || (pwd = getpwnam(user)) == NULL)
172                 return (PAM_SERVICE_ERR);
173
174         PAM_LOG("Got user: %s", user);
175
176         retval = pam_get_item(pamh, PAM_RHOST, &rhost);
177         if (retval != PAM_SUCCESS)
178                 return (retval);
179
180         retval = pam_get_item(pamh, PAM_TTY, &tty);
181         if (retval != PAM_SUCCESS)
182                 return (retval);
183
184         if (*pwd->pw_passwd == '\0' &&
185             (flags & PAM_DISALLOW_NULL_AUTHTOK) != 0)
186                 return (PAM_NEW_AUTHTOK_REQD);
187
188         if (strncmp(pwd->pw_passwd, LOCKED_PREFIX, LOCKED_PREFIX_LEN) == 0)
189                 return (PAM_AUTH_ERR);
190
191         lc = login_getpwclass(pwd);
192         if (lc == NULL) {
193                 PAM_LOG("Unable to get login class for user %s", user);
194                 return (PAM_SERVICE_ERR);
195         }
196
197         PAM_LOG("Got login_cap");
198
199         if (pwd->pw_change || pwd->pw_expire)
200                 gettimeofday(&tp, NULL);
201
202         /*
203          * Check pw_expire before pw_change - no point in letting the
204          * user change the password on an expired account.
205          */
206
207         if (pwd->pw_expire) {
208                 warntime = login_getcaptime(lc, "warnexpire",
209                     DEFAULT_WARN, DEFAULT_WARN);
210                 if (tp.tv_sec >= pwd->pw_expire) {
211                         login_close(lc);
212                         return (PAM_ACCT_EXPIRED);
213                 } else if (pwd->pw_expire - tp.tv_sec < warntime &&
214                     (flags & PAM_SILENT) == 0) {
215                         pam_error(pamh, "Warning: your account expires on %s",
216                             ctime(&pwd->pw_expire));
217                 }
218         }
219
220         retval = PAM_SUCCESS;
221         if (pwd->pw_change) {
222                 warntime = login_getcaptime(lc, "warnpassword",
223                     DEFAULT_WARN, DEFAULT_WARN);
224                 if (tp.tv_sec >= pwd->pw_change) {
225                         retval = PAM_NEW_AUTHTOK_REQD;
226                 } else if (pwd->pw_change - tp.tv_sec < warntime &&
227                     (flags & PAM_SILENT) == 0) {
228                         pam_error(pamh, "Warning: your password expires on %s",
229                             ctime(&pwd->pw_change));
230                 }
231         }
232
233         /*
234          * From here on, we must leave retval untouched (unless we
235          * know we're going to fail), because we need to remember
236          * whether we're supposed to return PAM_SUCCESS or
237          * PAM_NEW_AUTHTOK_REQD.
238          */
239
240         if (rhost && *(const char *)rhost != '\0') {
241                 memset(&hints, 0, sizeof(hints));
242                 hints.ai_family = AF_UNSPEC;
243                 if (getaddrinfo(rhost, NULL, &hints, &res) == 0) {
244                         getnameinfo(res->ai_addr, res->ai_addrlen,
245                             rhostip, sizeof(rhostip), NULL, 0,
246                             NI_NUMERICHOST);
247                 }
248                 if (res != NULL)
249                         freeaddrinfo(res);
250         }
251
252         /*
253          * Check host / tty / time-of-day restrictions
254          */
255
256         if (!auth_hostok(lc, rhost, rhostip) ||
257             !auth_ttyok(lc, tty) ||
258             !auth_timeok(lc, time(NULL)))
259                 retval = PAM_AUTH_ERR;
260
261         login_close(lc);
262
263         return (retval);
264 }
265
266 /*
267  * password management
268  *
269  * standard Unix and NIS password changing
270  */
271 PAM_EXTERN int
272 pam_sm_chauthtok(pam_handle_t *pamh, int flags,
273     int argc __unused, const char *argv[] __unused)
274 {
275 #ifdef YP
276         struct ypclnt *ypclnt;
277         const void *yp_domain, *yp_server;
278 #endif
279         char salt[SALTSIZE + 1];
280         login_cap_t *lc;
281         struct passwd *pwd, *old_pwd;
282         const char *user, *old_pass, *new_pass;
283         char *encrypted;
284         time_t passwordtime;
285         int pfd, tfd, retval;
286
287         if (openpam_get_option(pamh, PAM_OPT_AUTH_AS_SELF))
288                 user = getlogin();
289         else {
290                 retval = pam_get_user(pamh, &user, NULL);
291                 if (retval != PAM_SUCCESS)
292                         return (retval);
293         }
294         pwd = getpwnam(user);
295
296         if (pwd == NULL)
297                 return (PAM_AUTHTOK_RECOVERY_ERR);
298
299         PAM_LOG("Got user: %s", user);
300
301         if (flags & PAM_PRELIM_CHECK) {
302
303                 PAM_LOG("PRELIM round");
304
305                 if (getuid() == 0 &&
306                     (pwd->pw_fields & _PWF_SOURCE) == _PWF_FILES)
307                         /* root doesn't need the old password */
308                         return (pam_set_item(pamh, PAM_OLDAUTHTOK, ""));
309 #ifdef YP
310                 if (getuid() == 0 &&
311                     (pwd->pw_fields & _PWF_SOURCE) == _PWF_NIS) {
312
313                         yp_domain = yp_server = NULL;
314                         (void)pam_get_data(pamh, "yp_domain", &yp_domain);
315                         (void)pam_get_data(pamh, "yp_server", &yp_server);
316
317                         ypclnt = ypclnt_new(yp_domain, "passwd.byname", yp_server);
318                         if (ypclnt == NULL)
319                                 return (PAM_BUF_ERR);
320
321                         if (ypclnt_connect(ypclnt) == -1) {
322                                 ypclnt_free(ypclnt);
323                                 return (PAM_SERVICE_ERR);
324                         }
325
326                         retval = ypclnt_havepasswdd(ypclnt);
327                         ypclnt_free(ypclnt);
328                         if (retval == 1)
329                                 return (pam_set_item(pamh, PAM_OLDAUTHTOK, ""));
330                         else if (retval == -1)
331                                 return (PAM_SERVICE_ERR);
332                 }
333 #endif
334                 if (pwd->pw_passwd[0] == '\0'
335                     && openpam_get_option(pamh, PAM_OPT_NULLOK)) {
336                         /*
337                          * No password case. XXX Are we giving too much away
338                          * by not prompting for a password?
339                          * XXX check PAM_DISALLOW_NULL_AUTHTOK
340                          */
341                         old_pass = "";
342                         retval = PAM_SUCCESS;
343                 } else {
344                         retval = pam_get_authtok(pamh,
345                             PAM_OLDAUTHTOK, &old_pass, NULL);
346                         if (retval != PAM_SUCCESS)
347                                 return (retval);
348                 }
349                 PAM_LOG("Got old password");
350                 /* always encrypt first */
351                 encrypted = crypt(old_pass, pwd->pw_passwd);
352                 if (old_pass[0] == '\0' &&
353                     !openpam_get_option(pamh, PAM_OPT_NULLOK))
354                         return (PAM_PERM_DENIED);
355                 if (strcmp(encrypted, pwd->pw_passwd) != 0)
356                         return (PAM_PERM_DENIED);
357         }
358         else if (flags & PAM_UPDATE_AUTHTOK) {
359                 PAM_LOG("UPDATE round");
360
361                 retval = pam_get_authtok(pamh,
362                     PAM_OLDAUTHTOK, &old_pass, NULL);
363                 if (retval != PAM_SUCCESS)
364                         return (retval);
365                 PAM_LOG("Got old password");
366
367                 /* get new password */
368                 for (;;) {
369                         retval = pam_get_authtok(pamh,
370                             PAM_AUTHTOK, &new_pass, NULL);
371                         if (retval != PAM_TRY_AGAIN)
372                                 break;
373                         pam_error(pamh, "Mismatch; try again, EOF to quit.");
374                 }
375                 PAM_LOG("Got new password");
376                 if (retval != PAM_SUCCESS) {
377                         PAM_VERBOSE_ERROR("Unable to get new password");
378                         return (retval);
379                 }
380
381                 if (getuid() != 0 && new_pass[0] == '\0' &&
382                     !openpam_get_option(pamh, PAM_OPT_NULLOK))
383                         return (PAM_PERM_DENIED);
384
385                 if ((old_pwd = pw_dup(pwd)) == NULL)
386                         return (PAM_BUF_ERR);
387
388                 lc = login_getclass(pwd->pw_class);
389                 if (login_setcryptfmt(lc, password_hash, NULL) == NULL)
390                         openpam_log(PAM_LOG_ERROR,
391                             "can't set password cipher, relying on default");
392                 
393                 /* set password expiry date */
394                 pwd->pw_change = 0;
395                 passwordtime = login_getcaptime(lc, "passwordtime", 0, 0);
396                 if (passwordtime > 0)
397                         pwd->pw_change = time(NULL) + passwordtime;
398                 
399                 login_close(lc);
400                 makesalt(salt);
401                 pwd->pw_passwd = crypt(new_pass, salt);
402 #ifdef YP
403                 switch (old_pwd->pw_fields & _PWF_SOURCE) {
404                 case _PWF_FILES:
405 #endif
406                         retval = PAM_SERVICE_ERR;
407                         if (pw_init(NULL, NULL))
408                                 openpam_log(PAM_LOG_ERROR, "pw_init() failed");
409                         else if ((pfd = pw_lock()) == -1)
410                                 openpam_log(PAM_LOG_ERROR, "pw_lock() failed");
411                         else if ((tfd = pw_tmp(-1)) == -1)
412                                 openpam_log(PAM_LOG_ERROR, "pw_tmp() failed");
413                         else if (pw_copy(pfd, tfd, pwd, old_pwd) == -1)
414                                 openpam_log(PAM_LOG_ERROR, "pw_copy() failed");
415                         else if (pw_mkdb(pwd->pw_name) == -1)
416                                 openpam_log(PAM_LOG_ERROR, "pw_mkdb() failed");
417                         else
418                                 retval = PAM_SUCCESS;
419                         pw_fini();
420 #ifdef YP
421                         break;
422                 case _PWF_NIS:
423                         yp_domain = yp_server = NULL;
424                         (void)pam_get_data(pamh, "yp_domain", &yp_domain);
425                         (void)pam_get_data(pamh, "yp_server", &yp_server);
426                         ypclnt = ypclnt_new(yp_domain,
427                             "passwd.byname", yp_server);
428                         if (ypclnt == NULL) {
429                                 retval = PAM_BUF_ERR;
430                         } else if (ypclnt_connect(ypclnt) == -1 ||
431                             ypclnt_passwd(ypclnt, pwd, old_pass) == -1) {
432                                 openpam_log(PAM_LOG_ERROR, "%s", ypclnt->error);
433                                 retval = PAM_SERVICE_ERR;
434                         } else {
435                                 retval = PAM_SUCCESS;
436                         }
437                         ypclnt_free(ypclnt);
438                         break;
439                 default:
440                         openpam_log(PAM_LOG_ERROR, "unsupported source 0x%x",
441                             pwd->pw_fields & _PWF_SOURCE);
442                         retval = PAM_SERVICE_ERR;
443                 }
444 #endif
445                 free(old_pwd);
446         }
447         else {
448                 /* Very bad juju */
449                 retval = PAM_ABORT;
450                 PAM_LOG("Illegal 'flags'");
451         }
452
453         return (retval);
454 }
455
456 /* Mostly stolen from passwd(1)'s local_passwd.c - markm */
457
458 static unsigned char itoa64[] =         /* 0 ... 63 => ascii - 64 */
459         "./0123456789ABCDEFGHIJKLMNOPQRSTUVWXYZabcdefghijklmnopqrstuvwxyz";
460
461 static void
462 to64(char *s, long v, int n)
463 {
464         while (--n >= 0) {
465                 *s++ = itoa64[v&0x3f];
466                 v >>= 6;
467         }
468 }
469
470 /* Salt suitable for traditional DES and MD5 */
471 static void
472 makesalt(char salt[SALTSIZE + 1])
473 {
474         int i;
475
476         /* These are not really random numbers, they are just
477          * numbers that change to thwart construction of a
478          * dictionary.
479          */
480         for (i = 0; i < SALTSIZE; i += 4)
481                 to64(&salt[i], arc4random(), 4);
482         salt[SALTSIZE] = '\0';
483 }
484
485 PAM_MODULE_ENTRY("pam_unix");