pam_group.c 18 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362363364365366367368369370371372373374375376377378379380381382383384385386387388389390391392393394395396397398399400401402403404405406407408409410411412413414415416417418419420421422423424425426427428429430431432433434435436437438439440441442443444445446447448449450451452453454455456457458459460461462463464465466467468469470471472473474475476477478479480481482483484485486487488489490491492493494495496497498499500501502503504505506507508509510511512513514515516517518519520521522523524525526527528529530531532533534535536537538539540541542543544545546547548549550551552553554555556557558559560561562563564565566567568569570571572573574575576577578579580581582583584585586587588589590591592593594595596597598599600601602603604605606607608609610611612613614615616617618619620621622623624625626627628629630631632633634635636637638639640641642643644645646647648649650651652653654655656657658659660661662663664665666667668669670671672673674675676677678679680681682683684685686687688689690691692693694695696697698699700701702703704705706707708709710711712713714715716717718719720721722723724725726727728729730731732733734735736737738739740741742743744745746747748749750751752753754755756757758759760761762763764765766767768769770771772773774775776777778779780781782783784785786787788789790791792793794795796797798799800801802803804805806807808809810811812813814815
  1. /*
  2. * pam_group module
  3. *
  4. * Written by Andrew Morgan <morgan@linux.kernel.org> 1996/7/6
  5. * Field parsing rewritten by Tomas Mraz <tm@t8m.info>
  6. */
  7. #include "config.h"
  8. #include <sys/file.h>
  9. #include <stdio.h>
  10. #include <stdlib.h>
  11. #include <ctype.h>
  12. #include <unistd.h>
  13. #include <stdarg.h>
  14. #include <time.h>
  15. #include <syslog.h>
  16. #include <string.h>
  17. #include <grp.h>
  18. #include <sys/types.h>
  19. #include <sys/stat.h>
  20. #include <fcntl.h>
  21. #include <netdb.h>
  22. #define PAM_GROUP_BUFLEN 1000
  23. #define FIELD_SEPARATOR ';' /* this is new as of .02 */
  24. #ifndef TRUE
  25. # define TRUE 1
  26. #endif
  27. #ifndef FALSE
  28. # define FALSE 0
  29. #endif
  30. typedef enum { AND, OR } operator;
  31. #include <security/pam_modules.h>
  32. #include <security/_pam_macros.h>
  33. #include <security/pam_modutil.h>
  34. #include <security/pam_ext.h>
  35. /* --- static functions for checking whether the user should be let in --- */
  36. static char *
  37. shift_buf(char *mem, int from)
  38. {
  39. char *start = mem;
  40. while ((*mem = mem[from]) != '\0')
  41. ++mem;
  42. memset(mem, '\0', PAM_GROUP_BUFLEN - (mem - start));
  43. return mem;
  44. }
  45. static void
  46. trim_spaces(char *buf, char *from)
  47. {
  48. while (from > buf) {
  49. --from;
  50. if (*from == ' ')
  51. *from = '\0';
  52. else
  53. break;
  54. }
  55. }
  56. #define STATE_NL 0 /* new line starting */
  57. #define STATE_COMMENT 1 /* inside comment */
  58. #define STATE_FIELD 2 /* field following */
  59. #define STATE_EOF 3 /* end of file or error */
  60. static int
  61. read_field(const pam_handle_t *pamh, int fd, char **buf, int *from, int *state)
  62. {
  63. char *to;
  64. char *src;
  65. int i;
  66. char c;
  67. int onspace;
  68. /* is buf set ? */
  69. if (! *buf) {
  70. *buf = (char *) calloc(1, PAM_GROUP_BUFLEN+1);
  71. if (! *buf) {
  72. pam_syslog(pamh, LOG_CRIT, "out of memory");
  73. D(("no memory"));
  74. *state = STATE_EOF;
  75. return -1;
  76. }
  77. *from = 0;
  78. *state = STATE_NL;
  79. fd = open(PAM_GROUP_CONF, O_RDONLY);
  80. if (fd < 0) {
  81. pam_syslog(pamh, LOG_ERR, "error opening %s: %m", PAM_GROUP_CONF);
  82. _pam_drop(*buf);
  83. *state = STATE_EOF;
  84. return -1;
  85. }
  86. }
  87. if (*from > 0)
  88. to = shift_buf(*buf, *from);
  89. else
  90. to = *buf;
  91. while (fd != -1 && to - *buf < PAM_GROUP_BUFLEN) {
  92. i = pam_modutil_read(fd, to, PAM_GROUP_BUFLEN - (to - *buf));
  93. if (i < 0) {
  94. pam_syslog(pamh, LOG_ERR, "error reading %s: %m", PAM_GROUP_CONF);
  95. close(fd);
  96. memset(*buf, 0, PAM_GROUP_BUFLEN);
  97. _pam_drop(*buf);
  98. *state = STATE_EOF;
  99. return -1;
  100. } else if (!i) {
  101. close(fd);
  102. fd = -1; /* end of file reached */
  103. }
  104. to += i;
  105. }
  106. if (to == *buf) {
  107. /* nothing previously in buf, nothing read */
  108. _pam_drop(*buf);
  109. *state = STATE_EOF;
  110. return -1;
  111. }
  112. memset(to, '\0', PAM_GROUP_BUFLEN - (to - *buf));
  113. to = *buf;
  114. onspace = 1; /* delete any leading spaces */
  115. for (src = to; (c=*src) != '\0'; ++src) {
  116. if (*state == STATE_COMMENT && c != '\n') {
  117. continue;
  118. }
  119. switch (c) {
  120. case '\n':
  121. *state = STATE_NL;
  122. *to = '\0';
  123. *from = (src - *buf) + 1;
  124. trim_spaces(*buf, to);
  125. return fd;
  126. case '\t':
  127. case ' ':
  128. if (!onspace) {
  129. onspace = 1;
  130. *to++ = ' ';
  131. }
  132. break;
  133. case '!':
  134. onspace = 1; /* ignore following spaces */
  135. *to++ = '!';
  136. break;
  137. case '#':
  138. *state = STATE_COMMENT;
  139. break;
  140. case FIELD_SEPARATOR:
  141. *state = STATE_FIELD;
  142. *to = '\0';
  143. *from = (src - *buf) + 1;
  144. trim_spaces(*buf, to);
  145. return fd;
  146. case '\\':
  147. if (src[1] == '\n') {
  148. ++src; /* skip it */
  149. break;
  150. }
  151. /* fallthrough */
  152. default:
  153. *to++ = c;
  154. onspace = 0;
  155. }
  156. if (src > to)
  157. *src = '\0'; /* clearing */
  158. }
  159. if (*state != STATE_COMMENT) {
  160. *state = STATE_COMMENT;
  161. pam_syslog(pamh, LOG_ERR, "field too long - ignored");
  162. **buf = '\0';
  163. } else {
  164. *to = '\0';
  165. trim_spaces(*buf, to);
  166. }
  167. *from = 0;
  168. return fd;
  169. }
  170. /* read a member from a field */
  171. static int logic_member(const char *string, int *at)
  172. {
  173. int c,to;
  174. int done=0;
  175. int token=0;
  176. to=*at;
  177. do {
  178. c = string[to++];
  179. switch (c) {
  180. case '\0':
  181. --to;
  182. done = 1;
  183. break;
  184. case '&':
  185. case '|':
  186. case '!':
  187. if (token) {
  188. --to;
  189. }
  190. done = 1;
  191. break;
  192. default:
  193. if (isalpha(c) || c == '*' || isdigit(c) || c == '_'
  194. || c == '-' || c == '.' || c == '/' || c == ':') {
  195. token = 1;
  196. } else if (token) {
  197. --to;
  198. done = 1;
  199. } else {
  200. ++*at;
  201. }
  202. }
  203. } while (!done);
  204. return to - *at;
  205. }
  206. typedef enum { VAL, OP } expect;
  207. static int
  208. logic_field (const pam_handle_t *pamh, const void *me,
  209. const char *x, int rule,
  210. int (*agrees)(const pam_handle_t *pamh, const void *,
  211. const char *, int, int))
  212. {
  213. int left=FALSE, right, not=FALSE;
  214. operator oper=OR;
  215. int at=0, l;
  216. expect next=VAL;
  217. while ((l = logic_member(x,&at))) {
  218. int c = x[at];
  219. if (next == VAL) {
  220. if (c == '!')
  221. not = !not;
  222. else if (isalpha(c) || c == '*' || isdigit(c) || c == '_'
  223. || c == '-' || c == '.' || c == '/' || c == ':') {
  224. right = not ^ agrees(pamh, me, x+at, l, rule);
  225. if (oper == AND)
  226. left &= right;
  227. else
  228. left |= right;
  229. next = OP;
  230. } else {
  231. pam_syslog(pamh, LOG_ERR,
  232. "garbled syntax; expected name (rule #%d)",
  233. rule);
  234. return FALSE;
  235. }
  236. } else { /* OP */
  237. switch (c) {
  238. case '&':
  239. oper = AND;
  240. break;
  241. case '|':
  242. oper = OR;
  243. break;
  244. default:
  245. pam_syslog(pamh, LOG_ERR,
  246. "garbled syntax; expected & or | (rule #%d)",
  247. rule);
  248. D(("%c at %d",c,at));
  249. return FALSE;
  250. }
  251. next = VAL;
  252. not = FALSE;
  253. }
  254. at += l;
  255. }
  256. return left;
  257. }
  258. static int
  259. is_same (const pam_handle_t *pamh UNUSED,
  260. const void *A, const char *b, int len, int rule UNUSED)
  261. {
  262. int i;
  263. const char *a;
  264. a = A;
  265. for (i=0; len > 0; ++i, --len) {
  266. if (b[i] != a[i]) {
  267. if (b[i++] == '*') {
  268. return (!--len || !strncmp(b+i,a+strlen(a)-len,len));
  269. } else
  270. return FALSE;
  271. }
  272. }
  273. /* Ok, we know that b is a substring from A and does not contain
  274. wildcards, but now the length of both strings must be the same,
  275. too. In this case it means, a[i] has to be the end of the string. */
  276. if (a[i] != '\0')
  277. return FALSE;
  278. return ( !len );
  279. }
  280. typedef struct {
  281. int day; /* array of 7 bits, one set for today */
  282. int minute; /* integer, hour*100+minute for now */
  283. } TIME;
  284. static struct day {
  285. const char *d;
  286. int bit;
  287. } const days[11] = {
  288. { "su", 01 },
  289. { "mo", 02 },
  290. { "tu", 04 },
  291. { "we", 010 },
  292. { "th", 020 },
  293. { "fr", 040 },
  294. { "sa", 0100 },
  295. { "wk", 076 },
  296. { "wd", 0101 },
  297. { "al", 0177 },
  298. { NULL, 0 }
  299. };
  300. static TIME time_now(void)
  301. {
  302. struct tm *local;
  303. time_t the_time;
  304. TIME this;
  305. the_time = time((time_t *)0); /* get the current time */
  306. local = localtime(&the_time);
  307. this.day = days[local->tm_wday].bit;
  308. this.minute = local->tm_hour*100 + local->tm_min;
  309. D(("day: 0%o, time: %.4d", this.day, this.minute));
  310. return this;
  311. }
  312. /* take the current date and see if the range "date" passes it */
  313. static int
  314. check_time (const pam_handle_t *pamh, const void *AT,
  315. const char *times, int len, int rule)
  316. {
  317. int not,pass;
  318. int marked_day, time_start, time_end;
  319. const TIME *at;
  320. int i,j=0;
  321. at = AT;
  322. D(("checking: 0%o/%.4d vs. %s", at->day, at->minute, times));
  323. if (times == NULL) {
  324. /* this should not happen */
  325. pam_syslog(pamh, LOG_CRIT, "internal error in file %s at line %d",
  326. __FILE__, __LINE__);
  327. return FALSE;
  328. }
  329. if (times[j] == '!') {
  330. ++j;
  331. not = TRUE;
  332. } else {
  333. not = FALSE;
  334. }
  335. for (marked_day = 0; len > 0 && isalpha(times[j]); --len) {
  336. int this_day=-1;
  337. D(("%c%c ?", times[j], times[j+1]));
  338. for (i=0; days[i].d != NULL; ++i) {
  339. if (tolower(times[j]) == days[i].d[0]
  340. && tolower(times[j+1]) == days[i].d[1] ) {
  341. this_day = days[i].bit;
  342. break;
  343. }
  344. }
  345. j += 2;
  346. if (this_day == -1) {
  347. pam_syslog(pamh, LOG_ERR, "bad day specified (rule #%d)", rule);
  348. return FALSE;
  349. }
  350. marked_day ^= this_day;
  351. }
  352. if (marked_day == 0) {
  353. pam_syslog(pamh, LOG_ERR, "no day specified");
  354. return FALSE;
  355. }
  356. D(("day range = 0%o", marked_day));
  357. time_start = 0;
  358. for (i=0; len > 0 && i < 4 && isdigit(times[i+j]); ++i, --len) {
  359. time_start *= 10;
  360. time_start += times[i+j]-'0'; /* is this portable? */
  361. }
  362. j += i;
  363. if (times[j] == '-') {
  364. time_end = 0;
  365. for (i=1; len > 0 && i < 5 && isdigit(times[i+j]); ++i, --len) {
  366. time_end *= 10;
  367. time_end += times[i+j]-'0'; /* is this portable? */
  368. }
  369. j += i;
  370. } else
  371. time_end = -1;
  372. D(("i=%d, time_end=%d, times[j]='%c'", i, time_end, times[j]));
  373. if (i != 5 || time_end == -1) {
  374. pam_syslog(pamh, LOG_ERR, "no/bad times specified (rule #%d)", rule);
  375. return TRUE;
  376. }
  377. D(("times(%d to %d)", time_start,time_end));
  378. D(("marked_day = 0%o", marked_day));
  379. /* compare with the actual time now */
  380. pass = FALSE;
  381. if (time_start < time_end) { /* start < end ? --> same day */
  382. if ((at->day & marked_day) && (at->minute >= time_start)
  383. && (at->minute < time_end)) {
  384. D(("time is listed"));
  385. pass = TRUE;
  386. }
  387. } else { /* spans two days */
  388. if ((at->day & marked_day) && (at->minute >= time_start)) {
  389. D(("caught on first day"));
  390. pass = TRUE;
  391. } else {
  392. marked_day <<= 1;
  393. marked_day |= (marked_day & 0200) ? 1:0;
  394. D(("next day = 0%o", marked_day));
  395. if ((at->day & marked_day) && (at->minute <= time_end)) {
  396. D(("caught on second day"));
  397. pass = TRUE;
  398. }
  399. }
  400. }
  401. return (not ^ pass);
  402. }
  403. static int find_member(const char *string, int *at)
  404. {
  405. int c,to;
  406. int done=0;
  407. int token=0;
  408. to=*at;
  409. do {
  410. c = string[to++];
  411. switch (c) {
  412. case '\0':
  413. --to;
  414. done = 1;
  415. break;
  416. case '&':
  417. case '|':
  418. case '!':
  419. if (token) {
  420. --to;
  421. }
  422. done = 1;
  423. break;
  424. default:
  425. if (isalpha(c) || isdigit(c) || c == '_' || c == '*'
  426. || c == '-') {
  427. token = 1;
  428. } else if (token) {
  429. --to;
  430. done = 1;
  431. } else {
  432. ++*at;
  433. }
  434. }
  435. } while (!done);
  436. return to - *at;
  437. }
  438. #define GROUP_BLK 10
  439. #define blk_size(len) (((len-1 + GROUP_BLK)/GROUP_BLK)*GROUP_BLK)
  440. static int mkgrplist(pam_handle_t *pamh, char *buf, gid_t **list, int len)
  441. {
  442. int l,at=0;
  443. int blks;
  444. blks = blk_size(len);
  445. D(("cf. blks=%d and len=%d", blks,len));
  446. while ((l = find_member(buf,&at))) {
  447. int edge;
  448. if (len >= blks) {
  449. gid_t *tmp;
  450. D(("allocating new block"));
  451. tmp = (gid_t *) realloc((*list)
  452. , sizeof(gid_t) * (blks += GROUP_BLK));
  453. if (tmp != NULL) {
  454. (*list) = tmp;
  455. } else {
  456. pam_syslog(pamh, LOG_ERR, "out of memory for group list");
  457. free(*list);
  458. (*list) = NULL;
  459. return -1;
  460. }
  461. }
  462. /* '\0' terminate the entry */
  463. edge = (buf[at+l]) ? 1:0;
  464. buf[at+l] = '\0';
  465. D(("found group: %s",buf+at));
  466. /* this is where we convert a group name to a gid_t */
  467. {
  468. const struct group *grp;
  469. grp = pam_modutil_getgrnam(pamh, buf+at);
  470. if (grp == NULL) {
  471. pam_syslog(pamh, LOG_ERR, "bad group: %s", buf+at);
  472. } else {
  473. D(("group %s exists", buf+at));
  474. (*list)[len++] = grp->gr_gid;
  475. }
  476. }
  477. /* next entry along */
  478. at += l + edge;
  479. }
  480. D(("returning with [%p/len=%d]->%p",list,len,*list));
  481. return len;
  482. }
  483. static int check_account(pam_handle_t *pamh, const char *service,
  484. const char *tty, const char *user)
  485. {
  486. int from=0, state=STATE_NL, fd=-1;
  487. char *buffer=NULL;
  488. int count=0;
  489. TIME here_and_now;
  490. int retval=PAM_SUCCESS;
  491. gid_t *grps;
  492. int no_grps;
  493. /*
  494. * first we get the current list of groups - the application
  495. * will have previously done an initgroups(), or equivalent.
  496. */
  497. D(("counting supplementary groups"));
  498. no_grps = getgroups(0, NULL); /* find the current number of groups */
  499. if (no_grps > 0) {
  500. grps = calloc( blk_size(no_grps) , sizeof(gid_t) );
  501. D(("copying current list into grps [%d big]",blk_size(no_grps)));
  502. if (getgroups(no_grps, grps) < 0) {
  503. D(("getgroups call failed"));
  504. no_grps = 0;
  505. _pam_drop(grps);
  506. }
  507. #ifdef PAM_DEBUG
  508. {
  509. int z;
  510. for (z=0; z<no_grps; ++z) {
  511. D(("gid[%d]=%d", z, grps[z]));
  512. }
  513. }
  514. #endif
  515. } else {
  516. D(("no supplementary groups known"));
  517. no_grps = 0;
  518. grps = NULL;
  519. }
  520. here_and_now = time_now(); /* find current time */
  521. /* parse the rules in the configuration file */
  522. do {
  523. int good=TRUE;
  524. /* here we get the service name field */
  525. fd = read_field(pamh, fd, &buffer, &from, &state);
  526. if (!buffer || !buffer[0]) {
  527. /* empty line .. ? */
  528. continue;
  529. }
  530. ++count;
  531. D(("working on rule #%d",count));
  532. if (state != STATE_FIELD) {
  533. pam_syslog(pamh, LOG_ERR,
  534. "%s: malformed rule #%d", PAM_GROUP_CONF, count);
  535. continue;
  536. }
  537. good = logic_field(pamh,service, buffer, count, is_same);
  538. D(("with service: %s", good ? "passes":"fails" ));
  539. /* here we get the terminal name field */
  540. fd = read_field(pamh, fd, &buffer, &from, &state);
  541. if (state != STATE_FIELD) {
  542. pam_syslog(pamh, LOG_ERR,
  543. "%s: malformed rule #%d", PAM_GROUP_CONF, count);
  544. continue;
  545. }
  546. good &= logic_field(pamh,tty, buffer, count, is_same);
  547. D(("with tty: %s", good ? "passes":"fails" ));
  548. /* here we get the username field */
  549. fd = read_field(pamh, fd, &buffer, &from, &state);
  550. if (state != STATE_FIELD) {
  551. pam_syslog(pamh, LOG_ERR,
  552. "%s: malformed rule #%d", PAM_GROUP_CONF, count);
  553. continue;
  554. }
  555. /* If buffer starts with @, we are using netgroups */
  556. if (buffer[0] == '@')
  557. #ifdef HAVE_INNETGR
  558. good &= innetgr (&buffer[1], NULL, user, NULL);
  559. #else
  560. pam_syslog (pamh, LOG_ERR, "pam_group does not have netgroup support");
  561. #endif
  562. /* otherwise, if the buffer starts with %, it's a UNIX group */
  563. else if (buffer[0] == '%')
  564. good &= pam_modutil_user_in_group_nam_nam(pamh, user, &buffer[1]);
  565. else
  566. good &= logic_field(pamh,user, buffer, count, is_same);
  567. D(("with user: %s", good ? "passes":"fails" ));
  568. /* here we get the time field */
  569. fd = read_field(pamh, fd, &buffer, &from, &state);
  570. if (state != STATE_FIELD) {
  571. pam_syslog(pamh, LOG_ERR,
  572. "%s: malformed rule #%d", PAM_GROUP_CONF, count);
  573. continue;
  574. }
  575. good &= logic_field(pamh,&here_and_now, buffer, count, check_time);
  576. D(("with time: %s", good ? "passes":"fails" ));
  577. fd = read_field(pamh, fd, &buffer, &from, &state);
  578. if (state == STATE_FIELD) {
  579. pam_syslog(pamh, LOG_ERR,
  580. "%s: poorly terminated rule #%d", PAM_GROUP_CONF, count);
  581. continue;
  582. }
  583. /*
  584. * so we have a list of groups, we need to turn it into
  585. * something to send to setgroups(2)
  586. */
  587. if (good) {
  588. D(("adding %s to gid list", buffer));
  589. good = mkgrplist(pamh, buffer, &grps, no_grps);
  590. if (good < 0) {
  591. no_grps = 0;
  592. } else {
  593. no_grps = good;
  594. }
  595. }
  596. if (good > 0) {
  597. D(("rule #%d passed, added %d groups", count, good));
  598. } else if (good < 0) {
  599. retval = PAM_BUF_ERR;
  600. } else {
  601. D(("rule #%d failed", count));
  602. }
  603. } while (state != STATE_EOF);
  604. /* now set the groups for the user */
  605. if (no_grps > 0) {
  606. #ifdef PAM_DEBUG
  607. int err;
  608. #endif
  609. D(("trying to set %d groups", no_grps));
  610. #ifdef PAM_DEBUG
  611. for (err=0; err<no_grps; ++err) {
  612. D(("gid[%d]=%d", err, grps[err]));
  613. }
  614. #endif
  615. if (setgroups(no_grps, grps) != 0) {
  616. D(("but couldn't set groups %m"));
  617. pam_syslog(pamh, LOG_ERR,
  618. "unable to set the group membership for user: %m");
  619. retval = PAM_CRED_ERR;
  620. }
  621. }
  622. if (grps) { /* tidy up */
  623. memset(grps, 0, sizeof(gid_t) * blk_size(no_grps));
  624. _pam_drop(grps);
  625. no_grps = 0;
  626. }
  627. return retval;
  628. }
  629. /* --- public authentication management functions --- */
  630. int
  631. pam_sm_authenticate (pam_handle_t *pamh UNUSED, int flags UNUSED,
  632. int argc UNUSED, const char **argv UNUSED)
  633. {
  634. return PAM_IGNORE;
  635. }
  636. int
  637. pam_sm_setcred (pam_handle_t *pamh, int flags,
  638. int argc UNUSED, const char **argv UNUSED)
  639. {
  640. const void *service=NULL, *void_tty=NULL;
  641. const char *user=NULL;
  642. const char *tty;
  643. int retval;
  644. unsigned setting;
  645. /* only interested in establishing credentials */
  646. setting = flags;
  647. if (!(setting & (PAM_ESTABLISH_CRED | PAM_REINITIALIZE_CRED))) {
  648. D(("ignoring call - not for establishing credentials"));
  649. return PAM_SUCCESS; /* don't fail because of this */
  650. }
  651. /* set service name */
  652. if (pam_get_item(pamh, PAM_SERVICE, &service)
  653. != PAM_SUCCESS || service == NULL) {
  654. pam_syslog(pamh, LOG_ERR, "cannot find the current service name");
  655. return PAM_ABORT;
  656. }
  657. /* set username */
  658. if (pam_get_user(pamh, &user, NULL) != PAM_SUCCESS || *user == '\0') {
  659. pam_syslog(pamh, LOG_NOTICE, "cannot determine user name");
  660. return PAM_USER_UNKNOWN;
  661. }
  662. /* set tty name */
  663. if (pam_get_item(pamh, PAM_TTY, &void_tty) != PAM_SUCCESS
  664. || void_tty == NULL) {
  665. D(("PAM_TTY not set, probing stdin"));
  666. tty = ttyname(STDIN_FILENO);
  667. if (tty == NULL) {
  668. tty = "";
  669. }
  670. if (pam_set_item(pamh, PAM_TTY, tty) != PAM_SUCCESS) {
  671. pam_syslog(pamh, LOG_ERR, "couldn't set tty name");
  672. return PAM_ABORT;
  673. }
  674. }
  675. else
  676. tty = (const char *) void_tty;
  677. if (tty[0] == '/') { /* full path */
  678. const char *t;
  679. tty++;
  680. if ((t = strchr(tty, '/')) != NULL) {
  681. tty = t + 1;
  682. }
  683. }
  684. /* good, now we have the service name, the user and the terminal name */
  685. D(("service=%s", service));
  686. D(("user=%s", user));
  687. D(("tty=%s", tty));
  688. retval = check_account(pamh,service,tty,user); /* get groups */
  689. return retval;
  690. }
  691. /* end of module definition */