pam_userdb.c 15 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362363364365366367368369370371372373374375376377378379380381382383384385386387388389390391392393394395396397398399400401402403404405406407408409410411412413414415416417418419420421422423424425426427428429430431432433434435436437438439440441442443444445446447448449450451452453454455456457458459460461462463464465466467468469470471472473474475476477478479480481482483484485486487488489490491492493494495496497498499500501502503504505506507508509510511512513514515516517
  1. /*
  2. * pam_userdb module
  3. *
  4. * Written by Cristian Gafton <gafton@redhat.com> 1996/09/10
  5. * See the end of the file for Copyright Information
  6. */
  7. #include "config.h"
  8. #include <stdio.h>
  9. #include <stdlib.h>
  10. #include <unistd.h>
  11. #include <string.h>
  12. #include <syslog.h>
  13. #include <stdarg.h>
  14. #include <sys/types.h>
  15. #include <sys/stat.h>
  16. #include <fcntl.h>
  17. #include <errno.h>
  18. #ifdef HAVE_CRYPT_H
  19. #include <crypt.h>
  20. #endif
  21. #include "pam_userdb.h"
  22. #ifdef HAVE_NDBM_H
  23. # include <ndbm.h>
  24. #else
  25. # ifdef HAVE_DB_H
  26. # define DB_DBM_HSEARCH 1 /* use the dbm interface */
  27. # define HAVE_DBM /* for BerkDB 5.0 and later */
  28. # include <db.h>
  29. # else
  30. # error "failed to find a libdb or equivalent"
  31. # endif
  32. #endif
  33. #include <security/pam_modules.h>
  34. #include <security/pam_ext.h>
  35. #include <security/_pam_macros.h>
  36. #include "pam_inline.h"
  37. /*
  38. * Conversation function to obtain the user's password
  39. */
  40. static int
  41. obtain_authtok(pam_handle_t *pamh)
  42. {
  43. char *resp;
  44. const void *item;
  45. int retval;
  46. retval = pam_prompt(pamh, PAM_PROMPT_ECHO_OFF, &resp, _("Password: "));
  47. if (retval != PAM_SUCCESS)
  48. return retval;
  49. if (resp == NULL)
  50. return PAM_CONV_ERR;
  51. /* set the auth token */
  52. retval = pam_set_item(pamh, PAM_AUTHTOK, resp);
  53. /* clean it up */
  54. _pam_overwrite(resp);
  55. _pam_drop(resp);
  56. if ( (retval != PAM_SUCCESS) ||
  57. (retval = pam_get_item(pamh, PAM_AUTHTOK, &item))
  58. != PAM_SUCCESS ) {
  59. return retval;
  60. }
  61. return retval;
  62. }
  63. static int
  64. _pam_parse (pam_handle_t *pamh, int argc, const char **argv,
  65. const char **database, const char **cryptmode)
  66. {
  67. int ctrl;
  68. *database = NULL;
  69. *cryptmode = NULL;
  70. /* step through arguments */
  71. for (ctrl = 0; argc-- > 0; ++argv)
  72. {
  73. const char *str;
  74. /* generic options */
  75. if (!strcmp(*argv,"debug"))
  76. ctrl |= PAM_DEBUG_ARG;
  77. else if (!strcasecmp(*argv, "icase"))
  78. ctrl |= PAM_ICASE_ARG;
  79. else if (!strcasecmp(*argv, "dump"))
  80. ctrl |= PAM_DUMP_ARG;
  81. else if (!strcasecmp(*argv, "unknown_ok"))
  82. ctrl |= PAM_UNKNOWN_OK_ARG;
  83. else if (!strcasecmp(*argv, "key_only"))
  84. ctrl |= PAM_KEY_ONLY_ARG;
  85. else if (!strcasecmp(*argv, "use_first_pass"))
  86. ctrl |= PAM_USE_FPASS_ARG;
  87. else if (!strcasecmp(*argv, "try_first_pass"))
  88. ctrl |= PAM_TRY_FPASS_ARG;
  89. else if ((str = pam_str_skip_icase_prefix(*argv, "db=")) != NULL)
  90. {
  91. *database = str;
  92. if (**database == '\0') {
  93. *database = NULL;
  94. pam_syslog(pamh, LOG_ERR,
  95. "db= specification missing argument - ignored");
  96. }
  97. }
  98. else if ((str = pam_str_skip_icase_prefix(*argv, "crypt=")) != NULL)
  99. {
  100. *cryptmode = str;
  101. if (**cryptmode == '\0')
  102. pam_syslog(pamh, LOG_ERR,
  103. "crypt= specification missing argument - ignored");
  104. }
  105. else
  106. {
  107. pam_syslog(pamh, LOG_ERR, "unknown option: %s", *argv);
  108. }
  109. }
  110. return ctrl;
  111. }
  112. /*
  113. * Looks up a user name in a database and checks the password
  114. *
  115. * return values:
  116. * 1 = User not found
  117. * 0 = OK
  118. * -1 = Password incorrect
  119. * -2 = System error
  120. */
  121. static int
  122. user_lookup (pam_handle_t *pamh, const char *database, const char *cryptmode,
  123. const char *user, const char *pass, int ctrl)
  124. {
  125. DBM *dbm;
  126. datum key, data;
  127. /* Open the DB file. */
  128. dbm = dbm_open(database, O_RDONLY, 0644);
  129. if (dbm == NULL) {
  130. pam_syslog(pamh, LOG_ERR,
  131. "user_lookup: could not open database `%s': %m", database);
  132. return -2;
  133. }
  134. /* dump out the database contents for debugging */
  135. if (ctrl & PAM_DUMP_ARG) {
  136. pam_syslog(pamh, LOG_INFO, "Database dump:");
  137. for (key = dbm_firstkey(dbm); key.dptr != NULL;
  138. key = dbm_nextkey(dbm)) {
  139. data = dbm_fetch(dbm, key);
  140. pam_syslog(pamh, LOG_INFO,
  141. "key[len=%d] = `%s', data[len=%d] = `%s'",
  142. key.dsize, key.dptr, data.dsize, data.dptr);
  143. }
  144. }
  145. /* do some more init work */
  146. memset(&key, 0, sizeof(key));
  147. memset(&data, 0, sizeof(data));
  148. if (ctrl & PAM_KEY_ONLY_ARG) {
  149. if (asprintf(&key.dptr, "%s-%s", user, pass) < 0)
  150. key.dptr = NULL;
  151. else
  152. key.dsize = strlen(key.dptr);
  153. } else {
  154. key.dptr = strdup(user);
  155. key.dsize = strlen(user);
  156. }
  157. if (key.dptr) {
  158. data = dbm_fetch(dbm, key);
  159. memset(key.dptr, 0, key.dsize);
  160. free(key.dptr);
  161. }
  162. if (ctrl & PAM_DEBUG_ARG) {
  163. pam_syslog(pamh, LOG_INFO,
  164. "password in database is [%p]`%.*s', len is %d",
  165. data.dptr, data.dsize, (char *) data.dptr, data.dsize);
  166. }
  167. if (data.dptr != NULL) {
  168. int compare = -2;
  169. if (ctrl & PAM_KEY_ONLY_ARG)
  170. {
  171. dbm_close (dbm);
  172. return 0; /* found it, data contents don't matter */
  173. }
  174. if (cryptmode && pam_str_skip_icase_prefix(cryptmode, "crypt") != NULL) {
  175. /* crypt(3) password storage */
  176. char *cryptpw = NULL;
  177. if (data.dsize < 13) {
  178. /* hash is too short */
  179. pam_syslog(pamh, LOG_INFO, "password hash in database is too short");
  180. } else if (ctrl & PAM_ICASE_ARG) {
  181. pam_syslog(pamh, LOG_INFO,
  182. "case-insensitive comparison only works with plaintext passwords");
  183. } else {
  184. /* libdb is not guaranteed to produce null terminated strings */
  185. char *pwhash = strndup(data.dptr, data.dsize);
  186. if (pwhash == NULL) {
  187. pam_syslog(pamh, LOG_CRIT, "strndup failed: data.dptr");
  188. } else {
  189. #ifdef HAVE_CRYPT_R
  190. struct crypt_data *cdata = NULL;
  191. cdata = malloc(sizeof(*cdata));
  192. if (cdata == NULL) {
  193. pam_syslog(pamh, LOG_CRIT, "malloc failed: struct crypt_data");
  194. } else {
  195. cdata->initialized = 0;
  196. cryptpw = crypt_r(pass, pwhash, cdata);
  197. }
  198. #else
  199. cryptpw = crypt (pass, pwhash);
  200. #endif
  201. if (cryptpw && strlen(cryptpw) == (size_t)data.dsize) {
  202. compare = memcmp(data.dptr, cryptpw, data.dsize);
  203. } else {
  204. if (ctrl & PAM_DEBUG_ARG) {
  205. if (cryptpw) {
  206. pam_syslog(pamh, LOG_INFO, "lengths of computed and stored hashes differ");
  207. pam_syslog(pamh, LOG_INFO, "computed hash: %s", cryptpw);
  208. } else {
  209. pam_syslog(pamh, LOG_ERR, "crypt() returned NULL");
  210. }
  211. }
  212. }
  213. #ifdef HAVE_CRYPT_R
  214. free(cdata);
  215. #endif
  216. }
  217. free(pwhash);
  218. }
  219. } else {
  220. /* Unknown password encryption method -
  221. * default to plaintext password storage
  222. */
  223. if (strlen(pass) != (size_t)data.dsize) {
  224. compare = 1; /* wrong password len -> wrong password */
  225. } else if (ctrl & PAM_ICASE_ARG) {
  226. compare = strncasecmp(data.dptr, pass, data.dsize);
  227. } else {
  228. compare = strncmp(data.dptr, pass, data.dsize);
  229. }
  230. if (cryptmode && pam_str_skip_icase_prefix(cryptmode, "none") == NULL
  231. && (ctrl & PAM_DEBUG_ARG)) {
  232. pam_syslog(pamh, LOG_INFO, "invalid value for crypt parameter: %s",
  233. cryptmode);
  234. pam_syslog(pamh, LOG_INFO, "defaulting to plaintext password mode");
  235. }
  236. }
  237. dbm_close(dbm);
  238. if (compare == 0)
  239. return 0; /* match */
  240. else
  241. return -1; /* wrong */
  242. } else {
  243. int saw_user = 0;
  244. if (ctrl & PAM_DEBUG_ARG) {
  245. pam_syslog(pamh, LOG_INFO, "error returned by dbm_fetch: %m");
  246. }
  247. /* probably we should check dbm_error() here */
  248. if ((ctrl & PAM_KEY_ONLY_ARG) == 0) {
  249. dbm_close(dbm);
  250. return 1; /* not key_only, so no entry => no entry for the user */
  251. }
  252. /* now handle the key_only case */
  253. for (key = dbm_firstkey(dbm);
  254. key.dptr != NULL;
  255. key = dbm_nextkey(dbm)) {
  256. int compare;
  257. /* first compare the user portion (case sensitive) */
  258. compare = strncmp(key.dptr, user, strlen(user));
  259. if (compare == 0) {
  260. /* assume failure */
  261. compare = -1;
  262. /* if we have the divider where we expect it to be... */
  263. if (key.dptr[strlen(user)] == '-') {
  264. saw_user = 1;
  265. if ((size_t)key.dsize == strlen(user) + 1 + strlen(pass)) {
  266. if (ctrl & PAM_ICASE_ARG) {
  267. /* compare the password portion (case insensitive)*/
  268. compare = strncasecmp(key.dptr + strlen(user) + 1,
  269. pass,
  270. strlen(pass));
  271. } else {
  272. /* compare the password portion (case sensitive) */
  273. compare = strncmp(key.dptr + strlen(user) + 1,
  274. pass,
  275. strlen(pass));
  276. }
  277. }
  278. }
  279. if (compare == 0) {
  280. dbm_close(dbm);
  281. return 0; /* match */
  282. }
  283. }
  284. }
  285. dbm_close(dbm);
  286. if (saw_user)
  287. return -1; /* saw the user, but password mismatch */
  288. else
  289. return 1; /* not found */
  290. }
  291. /* NOT REACHED */
  292. return -2;
  293. }
  294. /* --- authentication management functions (only) --- */
  295. int
  296. pam_sm_authenticate(pam_handle_t *pamh, int flags UNUSED,
  297. int argc, const char **argv)
  298. {
  299. const char *username;
  300. const void *password;
  301. const char *database = NULL;
  302. const char *cryptmode = NULL;
  303. int retval = PAM_AUTH_ERR, ctrl;
  304. /* parse arguments */
  305. ctrl = _pam_parse(pamh, argc, argv, &database, &cryptmode);
  306. if (database == NULL) {
  307. pam_syslog(pamh, LOG_ERR, "can not get the database name");
  308. return PAM_SERVICE_ERR;
  309. }
  310. /* Get the username */
  311. retval = pam_get_user(pamh, &username, NULL);
  312. if (retval != PAM_SUCCESS) {
  313. pam_syslog(pamh, LOG_NOTICE, "cannot determine user name: %s",
  314. pam_strerror(pamh, retval));
  315. return PAM_SERVICE_ERR;
  316. }
  317. if ((ctrl & PAM_USE_FPASS_ARG) == 0 && (ctrl & PAM_TRY_FPASS_ARG) == 0) {
  318. /* Converse to obtain a password */
  319. retval = obtain_authtok(pamh);
  320. if (retval != PAM_SUCCESS) {
  321. pam_syslog(pamh, LOG_ERR, "can not obtain password from user");
  322. return retval;
  323. }
  324. }
  325. /* Check if we got a password */
  326. retval = pam_get_item(pamh, PAM_AUTHTOK, &password);
  327. if (retval != PAM_SUCCESS || password == NULL) {
  328. if ((ctrl & PAM_TRY_FPASS_ARG) != 0) {
  329. /* Converse to obtain a password */
  330. retval = obtain_authtok(pamh);
  331. if (retval != PAM_SUCCESS) {
  332. pam_syslog(pamh, LOG_ERR, "can not obtain password from user");
  333. return retval;
  334. }
  335. retval = pam_get_item(pamh, PAM_AUTHTOK, &password);
  336. }
  337. if (retval != PAM_SUCCESS || password == NULL) {
  338. pam_syslog(pamh, LOG_ERR, "can not recover user password");
  339. return PAM_AUTHTOK_RECOVERY_ERR;
  340. }
  341. }
  342. if (ctrl & PAM_DEBUG_ARG)
  343. pam_syslog(pamh, LOG_INFO, "Verify user `%s' with a password",
  344. username);
  345. /* Now use the username to look up password in the database file */
  346. retval = user_lookup(pamh, database, cryptmode, username, password, ctrl);
  347. switch (retval) {
  348. case -2:
  349. /* some sort of system error. The log was already printed */
  350. return PAM_SERVICE_ERR;
  351. case -1:
  352. /* incorrect password */
  353. pam_syslog(pamh, LOG_NOTICE,
  354. "user `%s' denied access (incorrect password)",
  355. username);
  356. return PAM_AUTH_ERR;
  357. case 1:
  358. /* the user does not exist in the database */
  359. if (ctrl & PAM_DEBUG_ARG)
  360. pam_syslog(pamh, LOG_NOTICE,
  361. "user `%s' not found in the database", username);
  362. return PAM_USER_UNKNOWN;
  363. case 0:
  364. /* Otherwise, the authentication looked good */
  365. pam_syslog(pamh, LOG_NOTICE, "user '%s' granted access", username);
  366. return PAM_SUCCESS;
  367. default:
  368. /* we don't know anything about this return value */
  369. pam_syslog(pamh, LOG_ERR,
  370. "internal module error (retval = %d, user = `%s'",
  371. retval, username);
  372. return PAM_SERVICE_ERR;
  373. }
  374. /* should not be reached */
  375. return PAM_IGNORE;
  376. }
  377. int
  378. pam_sm_setcred(pam_handle_t *pamh UNUSED, int flags UNUSED,
  379. int argc UNUSED, const char **argv UNUSED)
  380. {
  381. return PAM_SUCCESS;
  382. }
  383. int
  384. pam_sm_acct_mgmt(pam_handle_t *pamh, int flags UNUSED,
  385. int argc, const char **argv)
  386. {
  387. const char *username;
  388. const char *database = NULL;
  389. const char *cryptmode = NULL;
  390. int retval = PAM_AUTH_ERR, ctrl;
  391. /* parse arguments */
  392. ctrl = _pam_parse(pamh, argc, argv, &database, &cryptmode);
  393. /* Get the username */
  394. retval = pam_get_user(pamh, &username, NULL);
  395. if (retval != PAM_SUCCESS) {
  396. pam_syslog(pamh, LOG_NOTICE, "cannot determine user name: %s",
  397. pam_strerror(pamh, retval));
  398. return PAM_SERVICE_ERR;
  399. }
  400. /* Now use the username to look up password in the database file */
  401. retval = user_lookup(pamh, database, cryptmode, username, "", ctrl);
  402. switch (retval) {
  403. case -2:
  404. /* some sort of system error. The log was already printed */
  405. return PAM_SERVICE_ERR;
  406. case -1:
  407. /* incorrect password, but we don't care */
  408. /* FALL THROUGH */
  409. case 0:
  410. /* authentication succeeded. dumbest password ever. */
  411. return PAM_SUCCESS;
  412. case 1:
  413. /* the user does not exist in the database */
  414. return PAM_USER_UNKNOWN;
  415. default:
  416. /* we don't know anything about this return value */
  417. pam_syslog(pamh, LOG_ERR,
  418. "internal module error (retval = %d, user = `%s'",
  419. retval, username);
  420. return PAM_SERVICE_ERR;
  421. }
  422. return PAM_SUCCESS;
  423. }
  424. /*
  425. * Copyright (c) Cristian Gafton <gafton@redhat.com>, 1999
  426. * All rights reserved
  427. *
  428. * Redistribution and use in source and binary forms, with or without
  429. * modification, are permitted provided that the following conditions
  430. * are met:
  431. * 1. Redistributions of source code must retain the above copyright
  432. * notice, and the entire permission notice in its entirety,
  433. * including the disclaimer of warranties.
  434. * 2. Redistributions in binary form must reproduce the above copyright
  435. * notice, this list of conditions and the following disclaimer in the
  436. * documentation and/or other materials provided with the distribution.
  437. * 3. The name of the author may not be used to endorse or promote
  438. * products derived from this software without specific prior
  439. * written permission.
  440. *
  441. * ALTERNATIVELY, this product may be distributed under the terms of
  442. * the GNU Public License, in which case the provisions of the GPL are
  443. * required INSTEAD OF the above restrictions. (This clause is
  444. * necessary due to a potential bad interaction between the GPL and
  445. * the restrictions contained in a BSD-style copyright.)
  446. *
  447. * THIS SOFTWARE IS PROVIDED `AS IS'' AND ANY EXPRESS OR IMPLIED
  448. * WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES
  449. * OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE
  450. * DISCLAIMED. IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR ANY DIRECT,
  451. * INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES
  452. * (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR
  453. * SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION)
  454. * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT,
  455. * STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE)
  456. * ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED
  457. * OF THE POSSIBILITY OF SUCH DAMAGE.
  458. */