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