PageRenderTime 26ms CodeModel.GetById 22ms RepoModel.GetById 0ms app.codeStats 0ms

/usr/src/lib/pam_modules/dhkeys/dhkeys.c

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