1 /*
2 * CDDL HEADER START
3 *
4 * The contents of this file are subject to the terms of the
5 * Common Development and Distribution License (the "License").
6 * You may not use this file except in compliance with the License.
7 *
8 * You can obtain a copy of the license at usr/src/OPENSOLARIS.LICENSE
9 * or http://www.opensolaris.org/os/licensing.
10 * See the License for the specific language governing permissions
11 * and limitations under the License.
12 *
13 * When distributing Covered Code, include this CDDL HEADER in each
14 * file and include the License file at usr/src/OPENSOLARIS.LICENSE.
15 * If applicable, add the following below this CDDL HEADER, with the
16 * fields enclosed by brackets "[]" replaced with your own identifying
17 * information: Portions Copyright [yyyy] [name of copyright owner]
18 *
19 * CDDL HEADER END
20 */
21 /*
22 * Copyright 2009 Sun Microsystems, Inc. All rights reserved.
23 * Use is subject to license terms.
24 *
25 * Copyright 2023 OmniOS Community Edition (OmniOSce) Association.
26 */
27
28 #include <stdlib.h>
29 #include <syslog.h>
30 #include <errno.h>
31 #include <string.h>
32 #include <rpc/rpc.h>
33 #include <unistd.h>
34 #include <assert.h>
35 #include <stdarg.h>
36 #include <sys/types.h>
37 #include <sys/wait.h>
38 #include <limits.h>
39 #include <signal.h>
40 #include <pthread.h>
41 #include <synch.h>
42
43 #include <rpcsvc/nis.h>
44 #include <rpcsvc/yppasswd.h>
45 #include <rpcsvc/ypclnt.h>
46 #include <rpc/key_prot.h>
47 #include <rpc/rpc.h>
48 #include <nfs/nfs.h>
49 #include <nfs/nfssys.h>
50 #include <nss_dbdefs.h>
51 #include <nsswitch.h>
52 #include <rpcsvc/nis_dhext.h>
53
54 #include <security/pam_appl.h>
55 #include <security/pam_modules.h>
56 #include <security/pam_impl.h>
57
58 #include <libintl.h>
59
60 #include <sys/mman.h>
61
62 #include <passwdutil.h>
63
64 #include "key_call_uid.h"
65 #include <shadow.h>
66
67 extern int _nfssys(int, void *);
68
69 /*
70 * int msg(pamh, ...)
71 *
72 * display message to the user
73 */
74 /*PRINTFLIKE2*/
75 static int
msg(pam_handle_t * pamh,char * fmt,...)76 msg(pam_handle_t *pamh, char *fmt, ...)
77 {
78 va_list ap;
79 char messages[PAM_MAX_NUM_MSG][PAM_MAX_MSG_SIZE];
80
81 va_start(ap, fmt);
82 (void) vsnprintf(messages[0], sizeof (messages[0]), fmt, ap);
83 va_end(ap);
84
85 return (__pam_display_msg(pamh, PAM_ERROR_MSG, 1, messages, NULL));
86 }
87
88
89 /*
90 * Get the secret key for the given netname, key length, and algorithm
91 * type and send it to keyserv if the given pw decrypts it. Update the
92 * following counter args as necessary: get_seckey_cnt, good_pw_cnt, and
93 * set_seckey_cnt.
94 *
95 * Returns 0 on malloc failure, else 1.
96 */
97 static int
get_and_set_seckey(pam_handle_t * pamh,const char * netname,keylen_t keylen,algtype_t algtype,const char * pw,uid_t uid,gid_t gid,int * get_seckey_cnt,int * good_pw_cnt,int * set_seckey_cnt,int flags,int debug)98 get_and_set_seckey(
99 pam_handle_t *pamh, /* in */
100 const char *netname, /* in */
101 keylen_t keylen, /* in */
102 algtype_t algtype, /* in */
103 const char *pw, /* in */
104 uid_t uid, /* in */
105 gid_t gid, /* in */
106 int *get_seckey_cnt, /* out */
107 int *good_pw_cnt, /* out */
108 int *set_seckey_cnt, /* out */
109 int flags, /* in */
110 int debug) /* in */
111 {
112 char *skey;
113 int skeylen;
114 char messages[PAM_MAX_NUM_MSG][PAM_MAX_MSG_SIZE];
115
116 skeylen = BITS2NIBBLES(keylen) + 1;
117
118 if ((skey = malloc(skeylen)) == NULL) {
119 return (0);
120 }
121
122 if (getsecretkey_g(netname, keylen, algtype, skey, skeylen, pw)) {
123 (*get_seckey_cnt)++;
124
125 if (skey[0]) {
126 /* password does decrypt secret key */
127 (*good_pw_cnt)++;
128 if (key_setnet_g_uid(netname, skey, keylen, NULL, 0,
129 algtype, uid, gid) >= 0) {
130 (*set_seckey_cnt)++;
131 } else {
132 if (debug)
133 syslog(LOG_DEBUG, "pam_dhkeys: "
134 "get_and_set_seckey: could not "
135 "set secret key for keytype "
136 "%d-%d", keylen, algtype);
137 }
138 } else {
139 if (pamh && !(flags & PAM_SILENT)) {
140 (void) snprintf(messages[0],
141 sizeof (messages[0]),
142 dgettext(TEXT_DOMAIN,
143 "Password does not "
144 "decrypt secret key (type = %d-%d) "
145 "for '%s'."), keylen, algtype, netname);
146 (void) __pam_display_msg(pamh, PAM_ERROR_MSG, 1,
147 messages, NULL);
148 }
149 }
150 } else {
151 if (debug)
152 syslog(LOG_DEBUG, "pam_dhkeys: get_and_set_seckey: "
153 "could not get secret key for keytype %d-%d",
154 keylen, algtype);
155 }
156
157 free(skey);
158
159 return (1);
160 }
161
162 /*
163 * int establish_key(pamh, flags, debug, netname)
164 *
165 * This routine establishes the Secure RPC Credentials for the
166 * user specified in PAM_USER, using the password in PAM_AUTHTOK.
167 *
168 * Establishing RPC credentials is considered a "helper" function for the PAM
169 * stack so we should only return failures or PAM_IGNORE. Returning PAM_SUCCESS
170 * may short circuit the stack and circumvent later critical checks.
171 *
172 * we are called from pam_sm_setcred:
173 * 1. if we are root (uid == 0), we do nothing and return
174 * PAM_IGNORE.
175 * 2. else, we try to establish credentials.
176 *
177 * We return framework errors as appropriate such as PAM_USER_UNKNOWN,
178 * PAM_BUF_ERR, PAM_PERM_DENIED.
179 *
180 * If we succeed in establishing credentials we return PAM_IGNORE.
181 *
182 * If we fail to establish credentials then we return:
183 * - PAM_SERVICE_ERR (credentials needed) or PAM_SYSTEM_ERR
184 * (credentials not needed) if netname could not be created;
185 * - PAM_AUTH_ERR (credentials needed) or PAM_IGNORE (credentials
186 * not needed) if no credentials were retrieved;
187 * - PAM_AUTH_ERR if the password didn't decrypt the cred;
188 * - PAM_SYSTEM_ERR if the cred's could not be stored.
189 *
190 * This routine returns the user's netname in "netname".
191 *
192 * All tools--but the PAM stack--currently use getpass() to obtain
193 * the user's secure RPC password. We must make sure we don't use more than
194 * the first des_block (eight) characters of whatever is handed down to us.
195 * Therefore, we use a local variable "short_pass" to hold those 8 char's.
196 */
197 static int
establish_key(pam_handle_t * pamh,int flags,int debug,char * netname)198 establish_key(pam_handle_t *pamh, int flags, int debug, char *netname)
199 {
200 const char *user;
201 const char *passwd;
202 char short_pass[sizeof (des_block)+1], *short_passp;
203 int result;
204 uid_t uid;
205 gid_t gid;
206 int err;
207
208 struct passwd pw; /* Needed to obtain uid */
209 char *scratch;
210 int scratchlen;
211
212 mechanism_t **mechs;
213 mechanism_t **mpp;
214 int get_seckey_cnt = 0;
215 int set_seckey_cnt = 0;
216 int good_pw_cnt = 0;
217 int valid_mech_cnt = 0;
218
219 (void) pam_get_item(pamh, PAM_USER, (const void **)&user);
220
221 if (user == NULL || *user == '\0') {
222 if (debug)
223 syslog(LOG_DEBUG, "pam_dhkeys: user NULL or empty");
224 return (PAM_USER_UNKNOWN);
225 }
226
227 (void) pam_get_item(pamh, PAM_AUTHTOK, (const void **)&passwd);
228
229 scratchlen = sysconf(_SC_GETPW_R_SIZE_MAX);
230 if ((scratch = malloc(scratchlen)) == NULL)
231 return (PAM_BUF_ERR);
232
233 if (getpwnam_r(user, &pw, scratch, scratchlen) == NULL) {
234 result = PAM_USER_UNKNOWN;
235 goto out;
236 }
237
238 uid = pw.pw_uid;
239 gid = pw.pw_gid;
240
241 /*
242 * We don't set credentials when root logs in.
243 */
244 if (uid == 0) {
245 result = PAM_IGNORE;
246 goto out;
247 }
248
249 err = user2netname(netname, uid, NULL);
250
251 if (err != 1) {
252 if (debug)
253 syslog(LOG_DEBUG, "pam_dhkeys: user2netname failed");
254 result = PAM_SYSTEM_ERR;
255 goto out;
256 }
257
258 /* passwd can be NULL (no passwd or su as root) */
259 if (passwd) {
260 (void) strlcpy(short_pass, passwd, sizeof (short_pass));
261 short_passp = short_pass;
262 } else {
263 short_passp = NULL;
264 }
265
266 if (mechs = __nis_get_mechanisms(FALSE)) {
267
268 for (mpp = mechs; *mpp; mpp++) {
269 mechanism_t *mp = *mpp;
270
271 if (AUTH_DES_COMPAT_CHK(mp))
272 break; /* fall through to AUTH_DES below */
273
274 if (!VALID_MECH_ENTRY(mp))
275 continue;
276
277 if (debug)
278 syslog(LOG_DEBUG, "pam_dhkeys: trying "
279 "key type = %d-%d", mp->keylen,
280 mp->algtype);
281 valid_mech_cnt++;
282 if (!get_and_set_seckey(pamh, netname, mp->keylen,
283 mp->algtype, short_passp, uid, gid,
284 &get_seckey_cnt, &good_pw_cnt, &set_seckey_cnt,
285 flags, debug)) {
286 result = PAM_BUF_ERR;
287 goto out;
288 }
289 }
290 __nis_release_mechanisms(mechs);
291 /* fall through to AUTH_DES below */
292 } else {
293 /*
294 * No usable mechs found in security congifuration file thus
295 * fallback to AUTH_DES compat.
296 */
297 if (debug)
298 syslog(LOG_DEBUG, "pam_dhkeys: no valid mechs "
299 "found. Trying AUTH_DES.");
300 }
301
302 /*
303 * We always perform AUTH_DES for the benefit of services like NFS
304 * that may depend on the classic des 192bit key being set.
305 */
306 if (!get_and_set_seckey(pamh, netname, AUTH_DES_KEYLEN,
307 AUTH_DES_ALGTYPE, short_passp, uid, gid, &get_seckey_cnt,
308 &good_pw_cnt, &set_seckey_cnt, flags, debug)) {
309 result = PAM_BUF_ERR;
310 goto out;
311 }
312
313 if (debug) {
314 syslog(LOG_DEBUG, "pam_dhkeys: mech key totals:\n");
315 syslog(LOG_DEBUG, "pam_dhkeys: %d valid mechanism(s)",
316 valid_mech_cnt);
317 syslog(LOG_DEBUG, "pam_dhkeys: %d secret key(s) retrieved",
318 get_seckey_cnt);
319 syslog(LOG_DEBUG, "pam_dhkeys: %d passwd decrypt successes",
320 good_pw_cnt);
321 syslog(LOG_DEBUG, "pam_dhkeys: %d secret key(s) set",
322 set_seckey_cnt);
323 }
324
325 if (get_seckey_cnt == 0) { /* No credentials */
326 result = PAM_IGNORE;
327 goto out;
328 }
329
330 if (good_pw_cnt == 0) { /* wrong password */
331 result = PAM_AUTH_ERR;
332 goto out;
333 }
334
335 if (set_seckey_cnt == 0) {
336 result = PAM_SYSTEM_ERR;
337 goto out;
338 }
339 /* Credentials have been successfully established, return PAM_IGNORE */
340 result = PAM_IGNORE;
341 out:
342 /*
343 * If we are authenticating we attempt to establish credentials
344 * where appropriate. Failure to do so is only an error if we
345 * definitely needed them. Thus always return PAM_IGNORE
346 * if we are authenticating and credentials were not needed.
347 */
348 free(scratch);
349
350 (void) memset(short_pass, '\0', sizeof (short_pass));
351
352 return (result);
353 }
354
355 /*ARGSUSED*/
356 int
pam_sm_authenticate(pam_handle_t * pamh,int flags,int argc,const char ** argv)357 pam_sm_authenticate(pam_handle_t *pamh, int flags, int argc, const char **argv)
358 {
359 return (PAM_IGNORE);
360 }
361
362
363 typedef struct argres {
364 uid_t uid;
365 int result;
366 } argres_t;
367
368 /*
369 * Revoke NFS DES credentials.
370 * NFS may not be installed so we need to deal with SIGSYS
371 * when we call _nfssys(); we thus call _nfssys() in a seperate thread that
372 * is created specifically for this call. The thread specific signalmask
373 * is set to ignore SIGSYS. After the call to _nfssys(), the thread
374 * ceases to exist.
375 */
376 static void *
revoke_nfs_cred(void * ap)377 revoke_nfs_cred(void *ap)
378 {
379 struct nfs_revauth_args nra;
380 sigset_t isigset;
381 argres_t *argres = (argres_t *)ap;
382
383 nra.authtype = AUTH_DES;
384 nra.uid = argres->uid;
385
386 (void) sigemptyset(&isigset);
387 (void) sigaddset(&isigset, SIGSYS);
388
389 if (pthread_sigmask(SIG_BLOCK, &isigset, NULL) == 0) {
390 argres->result = _nfssys(NFS_REVAUTH, &nra);
391 if (argres->result < 0 && errno == ENOSYS) {
392 argres->result = 0;
393 }
394 } else {
395 argres->result = -1;
396 }
397 return (NULL);
398 }
399
400 static int
remove_key(pam_handle_t * pamh,int flags,int debug)401 remove_key(pam_handle_t *pamh, int flags, int debug)
402 {
403 int result;
404 const char *uname;
405 attrlist attr_pw[2];
406 const struct pam_repository *auth_rep = NULL;
407 pwu_repository_t *pwu_rep;
408 uid_t uid;
409 gid_t gid;
410 argres_t argres;
411 pthread_t tid;
412
413 (void) pam_get_item(pamh, PAM_USER, (const void **)&uname);
414 if (uname == NULL || *uname == '\0') {
415 if (debug)
416 syslog(LOG_DEBUG,
417 "pam_dhkeys: user NULL or empty in remove_key()");
418 return (PAM_USER_UNKNOWN);
419 }
420
421 if (strcmp(uname, "root") == 0) {
422 if ((flags & PAM_SILENT) == 0) {
423 char msg[3][PAM_MAX_MSG_SIZE];
424 (void) snprintf(msg[0], sizeof (msg[0]),
425 dgettext(TEXT_DOMAIN,
426 "removing root credentials would"
427 " break the rpc services that"));
428 (void) snprintf(msg[1], sizeof (msg[1]),
429 dgettext(TEXT_DOMAIN,
430 "use secure rpc on this host!"));
431 (void) snprintf(msg[2], sizeof (msg[2]),
432 dgettext(TEXT_DOMAIN,
433 "root may use keylogout -f to do"
434 " this (at your own risk)!"));
435 (void) __pam_display_msg(pamh, PAM_ERROR_MSG, 3,
436 msg, NULL);
437 }
438 return (PAM_PERM_DENIED);
439 }
440
441 (void) pam_get_item(pamh, PAM_REPOSITORY, (const void **)&auth_rep);
442 if (auth_rep != NULL) {
443 if ((pwu_rep = calloc(1, sizeof (*pwu_rep))) == NULL)
444 return (PAM_BUF_ERR);
445 pwu_rep->type = auth_rep->type;
446 pwu_rep->scope = auth_rep->scope;
447 pwu_rep->scope_len = auth_rep->scope_len;
448 } else {
449 pwu_rep = PWU_DEFAULT_REP;
450 }
451
452 /* Retrieve user's uid/gid from the password repository */
453 attr_pw[0].type = ATTR_UID; attr_pw[0].next = &attr_pw[1];
454 attr_pw[1].type = ATTR_GID; attr_pw[1].next = NULL;
455
456 result = __get_authtoken_attr(uname, pwu_rep, attr_pw);
457
458 if (pwu_rep != PWU_DEFAULT_REP)
459 free(pwu_rep);
460
461 if (result == PWU_NOT_FOUND)
462 return (PAM_USER_UNKNOWN);
463 if (result == PWU_DENIED)
464 return (PAM_PERM_DENIED);
465 if (result != PWU_SUCCESS)
466 return (PAM_SYSTEM_ERR);
467
468 uid = (uid_t)attr_pw[0].data.val_i;
469 gid = (gid_t)attr_pw[1].data.val_i;
470
471 (void) key_removesecret_g_uid(uid, gid);
472
473 argres.uid = uid;
474 argres.result = -1;
475
476 if (pthread_create(&tid, NULL, revoke_nfs_cred, (void *)&argres) == 0)
477 (void) pthread_join(tid, NULL);
478
479 if (argres.result < 0) {
480 if ((flags & PAM_SILENT) == 0) {
481 (void) msg(pamh, dgettext(TEXT_DOMAIN,
482 "Warning: NFS credentials not destroyed"));
483 }
484 return (PAM_AUTH_ERR);
485 }
486
487 return (PAM_IGNORE);
488 }
489
490 int
pam_sm_setcred(pam_handle_t * pamh,int flags,int argc,const char ** argv)491 pam_sm_setcred(pam_handle_t *pamh, int flags, int argc, const char **argv)
492 {
493 int i;
494 int debug = 0;
495 int result;
496 char netname[MAXNETNAMELEN + 1];
497
498 for (i = 0; i < argc; i++) {
499 if (strcmp(argv[i], "debug") == 0)
500 debug = 1;
501 else if (strcmp(argv[i], "nowarn") == 0)
502 flags |= PAM_SILENT;
503 }
504
505 /* Check for invalid flags */
506 if (flags && (flags & PAM_ESTABLISH_CRED) == 0 &&
507 (flags & PAM_REINITIALIZE_CRED) == 0 &&
508 (flags & PAM_REFRESH_CRED) == 0 &&
509 (flags & PAM_DELETE_CRED) == 0 &&
510 (flags & PAM_SILENT) == 0) {
511 syslog(LOG_ERR, "pam_dhkeys: pam_setcred: illegal flags %d",
512 flags);
513 return (PAM_SYSTEM_ERR);
514 }
515
516
517 if ((flags & PAM_REINITIALIZE_CRED) || (flags & PAM_REFRESH_CRED)) {
518 /* doesn't apply to UNIX */
519 if (debug)
520 syslog(LOG_DEBUG, "pam_dhkeys: cred reinit/refresh "
521 "ignored\n");
522 return (PAM_IGNORE);
523 }
524
525 if (flags & PAM_DELETE_CRED) {
526 if (debug)
527 syslog(LOG_DEBUG, "pam_dhkeys: removing creds\n");
528 result = remove_key(pamh, flags, debug);
529 } else {
530 result = establish_key(pamh, flags, debug, netname);
531 /* Some diagnostics */
532 if ((flags & PAM_SILENT) == 0) {
533 if (result == PAM_AUTH_ERR)
534 (void) msg(pamh, dgettext(TEXT_DOMAIN,
535 "Password does not decrypt any secret "
536 "keys for %s."), netname);
537 else if (result == PAM_SYSTEM_ERR && netname[0])
538 (void) msg(pamh, dgettext(TEXT_DOMAIN,
539 "Could not set secret key(s) for %s. "
540 "The key server may be down."), netname);
541 }
542
543 /* Not having credentials set is not an error... */
544 result = PAM_IGNORE;
545 }
546
547 return (result);
548 }
549
550 /*ARGSUSED*/
551 void
rpc_cleanup(pam_handle_t * pamh,void * data,int pam_status)552 rpc_cleanup(pam_handle_t *pamh, void *data, int pam_status)
553 {
554 if (data) {
555 (void) memset(data, 0, strlen(data));
556 free(data);
557 }
558 }
559
560 /*ARGSUSED*/
561 int
pam_sm_chauthtok(pam_handle_t * pamh,int flags,int argc,const char ** argv)562 pam_sm_chauthtok(pam_handle_t *pamh, int flags, int argc, const char **argv)
563 {
564 return (PAM_IGNORE);
565 }
566