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.
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.
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]
22 * Copyright 2009 Sun Microsystems, Inc. All rights reserved.
23 * Use is subject to license terms.
34 #include <sys/types.h>
41 #include <rpcsvc/nis.h>
42 #include <rpcsvc/yppasswd.h>
43 #include <rpcsvc/ypclnt.h>
44 #include <rpc/key_prot.h>
47 #include <nfs/nfssys.h>
48 #include <nss_dbdefs.h>
50 #include <rpcsvc/nis_dhext.h>
52 #include <security/pam_appl.h>
53 #include <security/pam_modules.h>
54 #include <security/pam_impl.h>
60 #include <passwdutil.h>
62 #include "key_call_uid.h"
65 extern int _nfssys(int, void *);
70 * display message to the user
74 msg(pam_handle_t
*pamh
, char *fmt
, ...)
77 char messages
[PAM_MAX_NUM_MSG
][PAM_MAX_MSG_SIZE
];
80 (void) vsnprintf(messages
[0], sizeof (messages
[0]), fmt
, ap
);
83 return (__pam_display_msg(pamh
, PAM_ERROR_MSG
, 1, messages
, NULL
));
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
93 * Returns 0 on malloc failure, else 1.
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 */
104 int *get_seckey_cnt
, /* out */
105 int *good_pw_cnt
, /* out */
106 int *set_seckey_cnt
, /* out */
112 char messages
[PAM_MAX_NUM_MSG
][PAM_MAX_MSG_SIZE
];
114 skeylen
= BITS2NIBBLES(keylen
) + 1;
116 if ((skey
= malloc(skeylen
)) == NULL
) {
120 if (getsecretkey_g(netname
, keylen
, algtype
, skey
, skeylen
, pw
)) {
124 /* password does decrypt secret key */
126 if (key_setnet_g_uid(netname
, skey
, keylen
, NULL
, 0,
127 algtype
, uid
, gid
) >= 0) {
131 syslog(LOG_DEBUG
, "pam_dhkeys: "
132 "get_and_set_seckey: could not "
133 "set secret key for keytype "
134 "%d-%d", keylen
, algtype
);
137 if (pamh
&& !(flags
& PAM_SILENT
)) {
138 (void) snprintf(messages
[0],
139 sizeof (messages
[0]),
140 dgettext(TEXT_DOMAIN
,
142 "decrypt secret key (type = %d-%d) "
143 "for '%s'."), keylen
, algtype
, netname
);
144 (void) __pam_display_msg(pamh
, PAM_ERROR_MSG
, 1,
150 syslog(LOG_DEBUG
, "pam_dhkeys: get_and_set_seckey: "
151 "could not get secret key for keytype %d-%d",
161 * int establish_key(pamh, flags, debug, netname)
163 * This routine establishes the Secure RPC Credentials for the
164 * user specified in PAM_USER, using the password in PAM_AUTHTOK.
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.
170 * we are called from pam_sm_setcred:
171 * 1. if we are root (uid == 0), we do nothing and return
173 * 2. else, we try to establish credentials.
175 * We return framework errors as appropriate such as PAM_USER_UNKNOWN,
176 * PAM_BUF_ERR, PAM_PERM_DENIED.
178 * If we succeed in establishing credentials we return PAM_IGNORE.
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.
188 * This routine returns the user's netname in "netname".
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.
196 establish_key(pam_handle_t
*pamh
, int flags
, int debug
, char *netname
)
200 char short_pass
[sizeof (des_block
)+1], *short_passp
;
206 struct passwd pw
; /* Needed to obtain uid */
213 int get_seckey_cnt
= 0;
214 int set_seckey_cnt
= 0;
216 int valid_mech_cnt
= 0;
218 (void) pam_get_item(pamh
, PAM_USER
, (void **)&user
);
220 if (user
== NULL
|| *user
== '\0') {
222 syslog(LOG_DEBUG
, "pam_dhkeys: user NULL or empty");
223 return (PAM_USER_UNKNOWN
);
226 (void) pam_get_item(pamh
, PAM_AUTHTOK
, (void **)&passwd
);
228 scratchlen
= sysconf(_SC_GETPW_R_SIZE_MAX
);
229 if ((scratch
= malloc(scratchlen
)) == NULL
)
230 return (PAM_BUF_ERR
);
232 getpwnam_r(user
, &pw
, scratch
, scratchlen
, &pwp
);
234 result
= PAM_USER_UNKNOWN
;
242 * We don't set credentials when root logs in.
249 err
= user2netname(netname
, uid
, NULL
);
253 syslog(LOG_DEBUG
, "pam_dhkeys: user2netname failed");
254 result
= PAM_SYSTEM_ERR
;
258 /* passwd can be NULL (no passwd or su as root) */
260 (void) strlcpy(short_pass
, passwd
, sizeof (short_pass
));
261 short_passp
= short_pass
;
265 if (mechs
= __nis_get_mechanisms(FALSE
)) {
267 for (mpp
= mechs
; *mpp
; mpp
++) {
268 mechanism_t
*mp
= *mpp
;
270 if (AUTH_DES_COMPAT_CHK(mp
))
271 break; /* fall through to AUTH_DES below */
273 if (!VALID_MECH_ENTRY(mp
))
277 syslog(LOG_DEBUG
, "pam_dhkeys: trying "
278 "key type = %d-%d", mp
->keylen
,
281 if (!get_and_set_seckey(pamh
, netname
, mp
->keylen
,
282 mp
->algtype
, short_passp
, uid
, gid
,
283 &get_seckey_cnt
, &good_pw_cnt
, &set_seckey_cnt
,
285 result
= PAM_BUF_ERR
;
289 __nis_release_mechanisms(mechs
);
290 /* fall through to AUTH_DES below */
293 * No usable mechs found in security congifuration file thus
294 * fallback to AUTH_DES compat.
297 syslog(LOG_DEBUG
, "pam_dhkeys: no valid mechs "
298 "found. Trying AUTH_DES.");
302 * We always perform AUTH_DES for the benefit of services like NFS
303 * that may depend on the classic des 192bit key being set.
305 if (!get_and_set_seckey(pamh
, netname
, AUTH_DES_KEYLEN
,
306 AUTH_DES_ALGTYPE
, short_passp
, uid
, gid
, &get_seckey_cnt
,
307 &good_pw_cnt
, &set_seckey_cnt
, flags
, debug
)) {
308 result
= PAM_BUF_ERR
;
313 syslog(LOG_DEBUG
, "pam_dhkeys: mech key totals:\n");
314 syslog(LOG_DEBUG
, "pam_dhkeys: %d valid mechanism(s)",
316 syslog(LOG_DEBUG
, "pam_dhkeys: %d secret key(s) retrieved",
318 syslog(LOG_DEBUG
, "pam_dhkeys: %d passwd decrypt successes",
320 syslog(LOG_DEBUG
, "pam_dhkeys: %d secret key(s) set",
324 if (get_seckey_cnt
== 0) { /* No credentials */
329 if (good_pw_cnt
== 0) { /* wrong password */
330 result
= PAM_AUTH_ERR
;
334 if (set_seckey_cnt
== 0) {
335 result
= PAM_SYSTEM_ERR
;
338 /* Credentials have been successfully established, return PAM_IGNORE */
342 * If we are authenticating we attempt to establish credentials
343 * where appropriate. Failure to do so is only an error if we
344 * definitely needed them. Thus always return PAM_IGNORE
345 * if we are authenticating and credentials were not needed.
349 (void) memset(short_pass
, '\0', sizeof (short_pass
));
356 pam_sm_authenticate(pam_handle_t
*pamh
, int flags
, int argc
, const char **argv
)
362 typedef struct argres
{
368 * Revoke NFS DES credentials.
369 * NFS may not be installed so we need to deal with SIGSYS
370 * when we call _nfssys(); we thus call _nfssys() in a seperate thread that
371 * is created specifically for this call. The thread specific signalmask
372 * is set to ignore SIGSYS. After the call to _nfssys(), the thread
376 revoke_nfs_cred(void *ap
)
378 struct nfs_revauth_args nra
;
380 argres_t
*argres
= (argres_t
*)ap
;
382 nra
.authtype
= AUTH_DES
;
383 nra
.uid
= argres
->uid
;
385 (void) sigemptyset(&isigset
);
386 (void) sigaddset(&isigset
, SIGSYS
);
388 if (pthread_sigmask(SIG_BLOCK
, &isigset
, NULL
) == 0) {
389 argres
->result
= _nfssys(NFS_REVAUTH
, &nra
);
390 if (argres
->result
< 0 && errno
== ENOSYS
) {
400 remove_key(pam_handle_t
*pamh
, int flags
, int debug
)
405 struct pam_repository
*auth_rep
= NULL
;
406 pwu_repository_t
*pwu_rep
;
412 (void) pam_get_item(pamh
, PAM_USER
, (void **)&uname
);
413 if (uname
== NULL
|| *uname
== '\0') {
416 "pam_dhkeys: user NULL or empty in remove_key()");
417 return (PAM_USER_UNKNOWN
);
420 if (strcmp(uname
, "root") == 0) {
421 if ((flags
& PAM_SILENT
) == 0) {
422 char msg
[3][PAM_MAX_MSG_SIZE
];
423 (void) snprintf(msg
[0], sizeof (msg
[0]),
424 dgettext(TEXT_DOMAIN
,
425 "removing root credentials would"
426 " break the rpc services that"));
427 (void) snprintf(msg
[1], sizeof (msg
[1]),
428 dgettext(TEXT_DOMAIN
,
429 "use secure rpc on this host!"));
430 (void) snprintf(msg
[2], sizeof (msg
[2]),
431 dgettext(TEXT_DOMAIN
,
432 "root may use keylogout -f to do"
433 " this (at your own risk)!"));
434 (void) __pam_display_msg(pamh
, PAM_ERROR_MSG
, 3,
437 return (PAM_PERM_DENIED
);
440 (void) pam_get_item(pamh
, PAM_REPOSITORY
, (void **)&auth_rep
);
441 if (auth_rep
!= NULL
) {
442 if ((pwu_rep
= calloc(1, sizeof (*pwu_rep
))) == NULL
)
443 return (PAM_BUF_ERR
);
444 pwu_rep
->type
= auth_rep
->type
;
445 pwu_rep
->scope
= auth_rep
->scope
;
446 pwu_rep
->scope_len
= auth_rep
->scope_len
;
448 pwu_rep
= PWU_DEFAULT_REP
;
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
;
454 result
= __get_authtoken_attr(uname
, pwu_rep
, attr_pw
);
456 if (pwu_rep
!= PWU_DEFAULT_REP
)
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
);
466 uid
= (uid_t
)attr_pw
[0].data
.val_i
;
467 gid
= (gid_t
)attr_pw
[1].data
.val_i
;
469 (void) key_removesecret_g_uid(uid
, gid
);
474 if (pthread_create(&tid
, NULL
, revoke_nfs_cred
, (void *)&argres
) == 0)
475 (void) pthread_join(tid
, NULL
);
477 if (argres
.result
< 0) {
478 if ((flags
& PAM_SILENT
) == 0) {
479 (void) msg(pamh
, dgettext(TEXT_DOMAIN
,
480 "Warning: NFS credentials not destroyed"));
482 return (PAM_AUTH_ERR
);
489 pam_sm_setcred(pam_handle_t
*pamh
, int flags
, int argc
, const char **argv
)
494 char netname
[MAXNETNAMELEN
+ 1];
496 for (i
= 0; i
< argc
; i
++) {
497 if (strcmp(argv
[i
], "debug") == 0)
499 else if (strcmp(argv
[i
], "nowarn") == 0)
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",
511 return (PAM_SYSTEM_ERR
);
515 if ((flags
& PAM_REINITIALIZE_CRED
) || (flags
& PAM_REFRESH_CRED
)) {
516 /* doesn't apply to UNIX */
518 syslog(LOG_DEBUG
, "pam_dhkeys: cred reinit/refresh "
523 if (flags
& PAM_DELETE_CRED
) {
525 syslog(LOG_DEBUG
, "pam_dhkeys: removing creds\n");
526 result
= remove_key(pamh
, flags
, debug
);
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
);
541 /* Not having credentials set is not an error... */
550 rpc_cleanup(pam_handle_t
*pamh
, void *data
, int pam_status
)
553 (void) memset(data
, 0, strlen(data
));
560 pam_sm_chauthtok(pam_handle_t
*pamh
, int flags
, int argc
, const char **argv
)