pam_env.c 24 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362363364365366367368369370371372373374375376377378379380381382383384385386387388389390391392393394395396397398399400401402403404405406407408409410411412413414415416417418419420421422423424425426427428429430431432433434435436437438439440441442443444445446447448449450451452453454455456457458459460461462463464465466467468469470471472473474475476477478479480481482483484485486487488489490491492493494495496497498499500501502503504505506507508509510511512513514515516517518519520521522523524525526527528529530531532533534535536537538539540541542543544545546547548549550551552553554555556557558559560561562563564565566567568569570571572573574575576577578579580581582583584585586587588589590591592593594595596597598599600601602603604605606607608609610611612613614615616617618619620621622623624625626627628629630631632633634635636637638639640641642643644645646647648649650651652653654655656657658659660661662663664665666667668669670671672673674675676677678679680681682683684685686687688689690691692693694695696697698699700701702703704705706707708709710711712713714715716717718719720721722723724725726727728729730731732733734735736737738739740741742743744745746747748749750751752753754755756757758759760761762763764765766767768769770771772773774775776777778779780781782783784785786787788789790791792793794795796797798799800801802803804805806807808809810811812813814815816817818819820821822823824825826827828829830831832833834835836837838839840841842843844845846847848849850851852853854855856857858859860861862863864865866867868869870871872873874875876877878879880881882883884885886887888889890891892893894
  1. /*
  2. * pam_env module
  3. *
  4. * Written by Dave Kinchlea <kinch@kinch.ark.com> 1997/01/31
  5. * Inspired by Andrew Morgan <morgan@kernel.org>, who also supplied the
  6. * template for this file (via pam_mail)
  7. */
  8. #define DEFAULT_ETC_ENVFILE "/etc/environment"
  9. #define DEFAULT_READ_ENVFILE 1
  10. #define DEFAULT_USER_ENVFILE ".pam_environment"
  11. #define DEFAULT_USER_READ_ENVFILE 0
  12. #include "config.h"
  13. #include <ctype.h>
  14. #include <errno.h>
  15. #include <pwd.h>
  16. #include <stdarg.h>
  17. #include <stdio.h>
  18. #include <stdlib.h>
  19. #include <string.h>
  20. #include <syslog.h>
  21. #include <sys/stat.h>
  22. #include <sys/types.h>
  23. #include <unistd.h>
  24. #include <security/pam_modules.h>
  25. #include <security/pam_modutil.h>
  26. #include <security/_pam_macros.h>
  27. #include <security/pam_ext.h>
  28. #include "pam_inline.h"
  29. /* This little structure makes it easier to keep variables together */
  30. typedef struct var {
  31. char *name;
  32. char *value;
  33. char *defval;
  34. char *override;
  35. } VAR;
  36. #define BUF_SIZE 8192
  37. #define MAX_ENV 8192
  38. #define GOOD_LINE 0
  39. #define BAD_LINE 100 /* This must be > the largest PAM_* error code */
  40. #define DEFINE_VAR 101
  41. #define UNDEFINE_VAR 102
  42. #define ILLEGAL_VAR 103
  43. static int _assemble_line(FILE *, char *, int);
  44. static int _parse_line(const pam_handle_t *, const char *, VAR *);
  45. static int _check_var(pam_handle_t *, VAR *); /* This is the real meat */
  46. static void _clean_var(VAR *);
  47. static int _expand_arg(pam_handle_t *, char **);
  48. static const char * _pam_get_item_byname(pam_handle_t *, const char *);
  49. static int _define_var(pam_handle_t *, int, VAR *);
  50. static int _undefine_var(pam_handle_t *, int, VAR *);
  51. /* This is a special value used to designate an empty string */
  52. static char quote='\0';
  53. /* argument parsing */
  54. #define PAM_DEBUG_ARG 0x01
  55. static int
  56. _pam_parse (const pam_handle_t *pamh, int argc, const char **argv,
  57. const char **conffile, const char **envfile, int *readenv,
  58. const char **user_envfile, int *user_readenv)
  59. {
  60. int ctrl=0;
  61. *user_envfile = DEFAULT_USER_ENVFILE;
  62. *envfile = DEFAULT_ETC_ENVFILE;
  63. *readenv = DEFAULT_READ_ENVFILE;
  64. *user_readenv = DEFAULT_USER_READ_ENVFILE;
  65. *conffile = DEFAULT_CONF_FILE;
  66. /* step through arguments */
  67. for (; argc-- > 0; ++argv) {
  68. const char *str;
  69. /* generic options */
  70. if (!strcmp(*argv,"debug"))
  71. ctrl |= PAM_DEBUG_ARG;
  72. else if ((str = pam_str_skip_prefix(*argv, "conffile=")) != NULL) {
  73. if (str[0] == '\0') {
  74. pam_syslog(pamh, LOG_ERR,
  75. "conffile= specification missing argument - ignored");
  76. } else {
  77. *conffile = str;
  78. D(("new Configuration File: %s", *conffile));
  79. }
  80. } else if ((str = pam_str_skip_prefix(*argv, "envfile=")) != NULL) {
  81. if (str[0] == '\0') {
  82. pam_syslog (pamh, LOG_ERR,
  83. "envfile= specification missing argument - ignored");
  84. } else {
  85. *envfile = str;
  86. D(("new Env File: %s", *envfile));
  87. }
  88. } else if ((str = pam_str_skip_prefix(*argv, "user_envfile=")) != NULL) {
  89. if (str[0] == '\0') {
  90. pam_syslog (pamh, LOG_ERR,
  91. "user_envfile= specification missing argument - ignored");
  92. } else {
  93. *user_envfile = str;
  94. D(("new User Env File: %s", *user_envfile));
  95. }
  96. } else if ((str = pam_str_skip_prefix(*argv, "readenv=")) != NULL) {
  97. *readenv = atoi(str);
  98. } else if ((str = pam_str_skip_prefix(*argv, "user_readenv=")) != NULL) {
  99. *user_readenv = atoi(str);
  100. } else
  101. pam_syslog(pamh, LOG_ERR, "unknown option: %s", *argv);
  102. }
  103. if (*user_readenv)
  104. pam_syslog(pamh, LOG_DEBUG, "deprecated reading of user environment enabled");
  105. return ctrl;
  106. }
  107. static int
  108. _parse_config_file(pam_handle_t *pamh, int ctrl, const char *file)
  109. {
  110. int retval;
  111. char buffer[BUF_SIZE];
  112. FILE *conf;
  113. VAR Var, *var=&Var;
  114. D(("Called."));
  115. var->name=NULL; var->defval=NULL; var->override=NULL;
  116. D(("Config file name is: %s", file));
  117. /*
  118. * Lets try to open the config file, parse it and process
  119. * any variables found.
  120. */
  121. if ((conf = fopen(file,"r")) == NULL) {
  122. pam_syslog(pamh, LOG_ERR, "Unable to open config file: %s: %m", file);
  123. return PAM_IGNORE;
  124. }
  125. /* _pam_assemble_line will provide a complete line from the config file,
  126. * with all comments removed and any escaped newlines fixed up
  127. */
  128. while (( retval = _assemble_line(conf, buffer, BUF_SIZE)) > 0) {
  129. D(("Read line: %s", buffer));
  130. if ((retval = _parse_line(pamh, buffer, var)) == GOOD_LINE) {
  131. retval = _check_var(pamh, var);
  132. if (DEFINE_VAR == retval) {
  133. retval = _define_var(pamh, ctrl, var);
  134. } else if (UNDEFINE_VAR == retval) {
  135. retval = _undefine_var(pamh, ctrl, var);
  136. }
  137. }
  138. if (PAM_SUCCESS != retval && ILLEGAL_VAR != retval
  139. && BAD_LINE != retval && PAM_BAD_ITEM != retval) break;
  140. _clean_var(var);
  141. } /* while */
  142. (void) fclose(conf);
  143. /* tidy up */
  144. _clean_var(var); /* We could have got here prematurely,
  145. * this is safe though */
  146. D(("Exit."));
  147. return (retval != 0 ? PAM_ABORT : PAM_SUCCESS);
  148. }
  149. static int
  150. _parse_env_file(pam_handle_t *pamh, int ctrl, const char *file)
  151. {
  152. int retval=PAM_SUCCESS, i, t;
  153. char buffer[BUF_SIZE], *key, *mark;
  154. FILE *conf;
  155. D(("Env file name is: %s", file));
  156. if ((conf = fopen(file,"r")) == NULL) {
  157. pam_syslog(pamh, LOG_ERR, "Unable to open env file: %s: %m", file);
  158. return PAM_IGNORE;
  159. }
  160. while (_assemble_line(conf, buffer, BUF_SIZE) > 0) {
  161. D(("Read line: %s", buffer));
  162. key = buffer;
  163. /* skip leading white space */
  164. key += strspn(key, " \n\t");
  165. /* skip blanks lines and comments */
  166. if (key[0] == '#')
  167. continue;
  168. /* skip over "export " if present so we can be compat with
  169. bash type declarations */
  170. if (strncmp(key, "export ", (size_t) 7) == 0)
  171. key += 7;
  172. /* now find the end of value */
  173. mark = key;
  174. while(mark[0] != '\n' && mark[0] != '#' && mark[0] != '\0')
  175. mark++;
  176. if (mark[0] != '\0')
  177. mark[0] = '\0';
  178. /*
  179. * sanity check, the key must be alphanumeric
  180. */
  181. if (key[0] == '=') {
  182. pam_syslog(pamh, LOG_ERR,
  183. "missing key name '%s' in %s', ignoring",
  184. key, file);
  185. continue;
  186. }
  187. for ( i = 0 ; key[i] != '=' && key[i] != '\0' ; i++ )
  188. if (!isalnum(key[i]) && key[i] != '_') {
  189. pam_syslog(pamh, LOG_ERR,
  190. "non-alphanumeric key '%s' in %s', ignoring",
  191. key, file);
  192. break;
  193. }
  194. /* non-alphanumeric key, ignore this line */
  195. if (key[i] != '=' && key[i] != '\0')
  196. continue;
  197. /* now we try to be smart about quotes around the value,
  198. but not too smart, we can't get all fancy with escaped
  199. values like bash */
  200. if (key[i] == '=' && (key[++i] == '\"' || key[i] == '\'')) {
  201. for ( t = i+1 ; key[t] != '\0' ; t++)
  202. if (key[t] != '\"' && key[t] != '\'')
  203. key[i++] = key[t];
  204. else if (key[t+1] != '\0')
  205. key[i++] = key[t];
  206. key[i] = '\0';
  207. }
  208. /* if this is a request to delete a variable, check that it's
  209. actually set first, so we don't get a vague error back from
  210. pam_putenv() */
  211. for (i = 0; key[i] != '=' && key[i] != '\0'; i++);
  212. if (key[i] == '\0' && !pam_getenv(pamh,key))
  213. continue;
  214. /* set the env var, if it fails, we break out of the loop */
  215. retval = pam_putenv(pamh, key);
  216. if (retval != PAM_SUCCESS) {
  217. D(("error setting env \"%s\"", key));
  218. break;
  219. } else if (ctrl & PAM_DEBUG_ARG) {
  220. pam_syslog(pamh, LOG_DEBUG,
  221. "pam_putenv(\"%s\")", key);
  222. }
  223. }
  224. (void) fclose(conf);
  225. /* tidy up */
  226. D(("Exit."));
  227. return retval;
  228. }
  229. /*
  230. * This is where we read a line of the PAM config file. The line may be
  231. * preceded by lines of comments and also extended with "\\\n"
  232. */
  233. static int _assemble_line(FILE *f, char *buffer, int buf_len)
  234. {
  235. char *p = buffer;
  236. char *s, *os;
  237. int used = 0;
  238. int whitespace;
  239. /* loop broken with a 'break' when a non-'\\n' ended line is read */
  240. D(("called."));
  241. for (;;) {
  242. if (used >= buf_len) {
  243. /* Overflow */
  244. D(("_assemble_line: overflow"));
  245. return -1;
  246. }
  247. if (fgets(p, buf_len - used, f) == NULL) {
  248. if (used) {
  249. /* Incomplete read */
  250. return -1;
  251. } else {
  252. /* EOF */
  253. return 0;
  254. }
  255. }
  256. if (p[0] == '\0') {
  257. D(("_assemble_line: corrupted or binary file"));
  258. return -1;
  259. }
  260. if (p[strlen(p)-1] != '\n' && !feof(f)) {
  261. D(("_assemble_line: line too long"));
  262. return -1;
  263. }
  264. /* skip leading spaces --- line may be blank */
  265. whitespace = strspn(p, " \n\t");
  266. s = p + whitespace;
  267. if (*s && (*s != '#')) {
  268. used += whitespace;
  269. os = s;
  270. /*
  271. * we are only interested in characters before the first '#'
  272. * character
  273. */
  274. while (*s && *s != '#')
  275. ++s;
  276. if (*s == '#') {
  277. *s = '\0';
  278. used += strlen(os);
  279. break; /* the line has been read */
  280. }
  281. s = os;
  282. /*
  283. * Check for backslash by scanning back from the end of
  284. * the entered line, the '\n' has been included since
  285. * normally a line is terminated with this
  286. * character. fgets() should only return one though!
  287. */
  288. s += strlen(s);
  289. while (s > os && ((*--s == ' ') || (*s == '\t')
  290. || (*s == '\n')));
  291. /* check if it ends with a backslash */
  292. if (*s == '\\') {
  293. *s = '\0'; /* truncate the line here */
  294. used += strlen(os);
  295. p = s; /* there is more ... */
  296. } else {
  297. /* End of the line! */
  298. used += strlen(os);
  299. break; /* this is the complete line */
  300. }
  301. } else {
  302. /* Nothing in this line */
  303. /* Don't move p */
  304. }
  305. }
  306. return used;
  307. }
  308. static int
  309. _parse_line (const pam_handle_t *pamh, const char *buffer, VAR *var)
  310. {
  311. /*
  312. * parse buffer into var, legal syntax is
  313. * VARIABLE [DEFAULT=[[string]] [OVERRIDE=[value]]
  314. *
  315. * Any other options defined make this a bad line,
  316. * error logged and no var set
  317. */
  318. int length, quoteflg=0;
  319. const char *ptr, *tmpptr;
  320. char **valptr;
  321. D(("Called buffer = <%s>", buffer));
  322. length = strcspn(buffer," \t\n");
  323. if ((var->name = malloc(length + 1)) == NULL) {
  324. pam_syslog(pamh, LOG_CRIT, "Couldn't malloc %d bytes", length+1);
  325. return PAM_BUF_ERR;
  326. }
  327. /*
  328. * The first thing on the line HAS to be the variable name,
  329. * it may be the only thing though.
  330. */
  331. strncpy(var->name, buffer, length);
  332. var->name[length] = '\0';
  333. D(("var->name = <%s>, length = %d", var->name, length));
  334. /*
  335. * Now we check for arguments, we only support two kinds and ('cause I am lazy)
  336. * each one can actually be listed any number of times
  337. */
  338. ptr = buffer+length;
  339. while ((length = strspn(ptr, " \t")) > 0) {
  340. ptr += length; /* remove leading whitespace */
  341. D((ptr));
  342. if ((tmpptr = pam_str_skip_prefix(ptr, "DEFAULT=")) != NULL) {
  343. ptr = tmpptr;
  344. D(("Default arg found: <%s>", ptr));
  345. valptr=&(var->defval);
  346. } else if ((tmpptr = pam_str_skip_prefix(ptr, "OVERRIDE=")) != NULL) {
  347. ptr = tmpptr;
  348. D(("Override arg found: <%s>", ptr));
  349. valptr=&(var->override);
  350. } else {
  351. D(("Unrecognized options: <%s> - ignoring line", ptr));
  352. pam_syslog(pamh, LOG_ERR, "Unrecognized Option: %s - ignoring line", ptr);
  353. return BAD_LINE;
  354. }
  355. if ('"' != *ptr) { /* Escaped quotes not supported */
  356. length = strcspn(ptr, " \t\n");
  357. tmpptr = ptr+length;
  358. } else {
  359. tmpptr = strchr(++ptr, '"');
  360. if (!tmpptr) {
  361. D(("Unterminated quoted string: %s", ptr-1));
  362. pam_syslog(pamh, LOG_ERR, "Unterminated quoted string: %s", ptr-1);
  363. return BAD_LINE;
  364. }
  365. length = tmpptr - ptr;
  366. if (*++tmpptr && ' ' != *tmpptr && '\t' != *tmpptr && '\n' != *tmpptr) {
  367. D(("Quotes must cover the entire string: <%s>", ptr));
  368. pam_syslog(pamh, LOG_ERR, "Quotes must cover the entire string: <%s>", ptr);
  369. return BAD_LINE;
  370. }
  371. quoteflg++;
  372. }
  373. if (length) {
  374. if ((*valptr = malloc(length + 1)) == NULL) {
  375. D(("Couldn't malloc %d bytes", length+1));
  376. pam_syslog(pamh, LOG_CRIT, "Couldn't malloc %d bytes", length+1);
  377. return PAM_BUF_ERR;
  378. }
  379. (void)strncpy(*valptr,ptr,length);
  380. (*valptr)[length]='\0';
  381. } else if (quoteflg--) {
  382. *valptr = &quote; /* a quick hack to handle the empty string */
  383. }
  384. ptr = tmpptr; /* Start the search where we stopped */
  385. } /* while */
  386. /*
  387. * The line is parsed, all is well.
  388. */
  389. D(("Exit."));
  390. ptr = NULL; tmpptr = NULL; valptr = NULL;
  391. return GOOD_LINE;
  392. }
  393. static int _check_var(pam_handle_t *pamh, VAR *var)
  394. {
  395. /*
  396. * Examine the variable and determine what action to take.
  397. * Returns DEFINE_VAR, UNDEFINE_VAR depending on action to take
  398. * or a PAM_* error code if passed back from other routines
  399. *
  400. * if no DEFAULT provided, the empty string is assumed
  401. * if no OVERRIDE provided, the empty string is assumed
  402. * if DEFAULT= and OVERRIDE evaluates to the empty string,
  403. * this variable should be undefined
  404. * if DEFAULT="" and OVERRIDE evaluates to the empty string,
  405. * this variable should be defined with no value
  406. * if OVERRIDE=value and value turns into the empty string, DEFAULT is used
  407. *
  408. * If DEFINE_VAR is to be returned, the correct value to define will
  409. * be pointed to by var->value
  410. */
  411. int retval;
  412. D(("Called."));
  413. /*
  414. * First thing to do is to expand any arguments, but only
  415. * if they are not the special quote values (cause expand_arg
  416. * changes memory).
  417. */
  418. if (var->defval && (&quote != var->defval) &&
  419. ((retval = _expand_arg(pamh, &(var->defval))) != PAM_SUCCESS)) {
  420. return retval;
  421. }
  422. if (var->override && (&quote != var->override) &&
  423. ((retval = _expand_arg(pamh, &(var->override))) != PAM_SUCCESS)) {
  424. return retval;
  425. }
  426. /* Now its easy */
  427. if (var->override && *(var->override)) {
  428. /* if there is a non-empty string in var->override, we use it */
  429. D(("OVERRIDE variable <%s> being used: <%s>", var->name, var->override));
  430. var->value = var->override;
  431. retval = DEFINE_VAR;
  432. } else {
  433. var->value = var->defval;
  434. if (&quote == var->defval) {
  435. /*
  436. * This means that the empty string was given for defval value
  437. * which indicates that a variable should be defined with no value
  438. */
  439. D(("An empty variable: <%s>", var->name));
  440. retval = DEFINE_VAR;
  441. } else if (var->defval) {
  442. D(("DEFAULT variable <%s> being used: <%s>", var->name, var->defval));
  443. retval = DEFINE_VAR;
  444. } else {
  445. D(("UNDEFINE variable <%s>", var->name));
  446. retval = UNDEFINE_VAR;
  447. }
  448. }
  449. D(("Exit."));
  450. return retval;
  451. }
  452. static int _expand_arg(pam_handle_t *pamh, char **value)
  453. {
  454. const char *orig=*value, *tmpptr=NULL;
  455. char *ptr; /*
  456. * Sure would be nice to use tmpptr but it needs to be
  457. * a constant so that the compiler will shut up when I
  458. * call pam_getenv and _pam_get_item_byname -- sigh
  459. */
  460. /* No unexpanded variable can be bigger than BUF_SIZE */
  461. char type, tmpval[BUF_SIZE];
  462. /* I know this shouldn't be hard-coded but it's so much easier this way */
  463. char tmp[MAX_ENV];
  464. size_t idx;
  465. D(("Remember to initialize tmp!"));
  466. memset(tmp, 0, MAX_ENV);
  467. idx = 0;
  468. /*
  469. * (possibly non-existent) environment variables can be used as values
  470. * by prepending a "$" and wrapping in {} (ie: ${HOST}), can escape with "\"
  471. * (possibly non-existent) PAM items can be used as values
  472. * by prepending a "@" and wrapping in {} (ie: @{PAM_RHOST}, can escape
  473. *
  474. */
  475. D(("Expanding <%s>",orig));
  476. while (*orig) { /* while there is some input to deal with */
  477. if ('\\' == *orig) {
  478. ++orig;
  479. if ('$' != *orig && '@' != *orig) {
  480. D(("Unrecognized escaped character: <%c> - ignoring", *orig));
  481. pam_syslog(pamh, LOG_ERR,
  482. "Unrecognized escaped character: <%c> - ignoring",
  483. *orig);
  484. } else if (idx + 1 < MAX_ENV) {
  485. tmp[idx++] = *orig++; /* Note the increment */
  486. } else {
  487. /* is it really a good idea to try to log this? */
  488. D(("Variable buffer overflow: <%s> + <%s>", tmp, tmpptr));
  489. pam_syslog (pamh, LOG_ERR, "Variable buffer overflow: <%s> + <%s>",
  490. tmp, tmpptr);
  491. return PAM_BUF_ERR;
  492. }
  493. continue;
  494. }
  495. if ('$' == *orig || '@' == *orig) {
  496. if ('{' != *(orig+1)) {
  497. D(("Expandable variables must be wrapped in {}"
  498. " <%s> - ignoring", orig));
  499. pam_syslog(pamh, LOG_ERR, "Expandable variables must be wrapped in {}"
  500. " <%s> - ignoring", orig);
  501. if (idx + 1 < MAX_ENV) {
  502. tmp[idx++] = *orig++; /* Note the increment */
  503. }
  504. continue;
  505. } else {
  506. D(("Expandable argument: <%s>", orig));
  507. type = *orig;
  508. orig+=2; /* skip the ${ or @{ characters */
  509. ptr = strchr(orig, '}');
  510. if (ptr) {
  511. *ptr++ = '\0';
  512. } else {
  513. D(("Unterminated expandable variable: <%s>", orig-2));
  514. pam_syslog(pamh, LOG_ERR,
  515. "Unterminated expandable variable: <%s>", orig-2);
  516. return PAM_ABORT;
  517. }
  518. strncpy(tmpval, orig, sizeof(tmpval));
  519. tmpval[sizeof(tmpval)-1] = '\0';
  520. orig=ptr;
  521. /*
  522. * so, we know we need to expand tmpval, it is either
  523. * an environment variable or a PAM_ITEM. type will tell us which
  524. */
  525. switch (type) {
  526. case '$':
  527. D(("Expanding env var: <%s>",tmpval));
  528. tmpptr = pam_getenv(pamh, tmpval);
  529. D(("Expanded to <%s>", tmpptr));
  530. break;
  531. case '@':
  532. D(("Expanding pam item: <%s>",tmpval));
  533. tmpptr = _pam_get_item_byname(pamh, tmpval);
  534. D(("Expanded to <%s>", tmpptr));
  535. break;
  536. default:
  537. D(("Impossible error, type == <%c>", type));
  538. pam_syslog(pamh, LOG_CRIT, "Impossible error, type == <%c>", type);
  539. return PAM_ABORT;
  540. } /* switch */
  541. if (tmpptr) {
  542. size_t len = strlen(tmpptr);
  543. if (idx + len < MAX_ENV) {
  544. strcpy(tmp + idx, tmpptr);
  545. idx += len;
  546. } else {
  547. /* is it really a good idea to try to log this? */
  548. D(("Variable buffer overflow: <%s> + <%s>", tmp, tmpptr));
  549. pam_syslog (pamh, LOG_ERR,
  550. "Variable buffer overflow: <%s> + <%s>", tmp, tmpptr);
  551. return PAM_BUF_ERR;
  552. }
  553. }
  554. } /* if ('{' != *orig++) */
  555. } else { /* if ( '$' == *orig || '@' == *orig) */
  556. if (idx + 1 < MAX_ENV) {
  557. tmp[idx++] = *orig++; /* Note the increment */
  558. } else {
  559. /* is it really a good idea to try to log this? */
  560. D(("Variable buffer overflow: <%s> + <%s>", tmp, tmpptr));
  561. pam_syslog(pamh, LOG_ERR,
  562. "Variable buffer overflow: <%s> + <%s>", tmp, tmpptr);
  563. return PAM_BUF_ERR;
  564. }
  565. }
  566. } /* for (;*orig;) */
  567. if (idx > strlen(*value)) {
  568. free(*value);
  569. if ((*value = malloc(idx + 1)) == NULL) {
  570. D(("Couldn't malloc %d bytes for expanded var", idx + 1));
  571. pam_syslog (pamh, LOG_CRIT, "Couldn't malloc %lu bytes for expanded var",
  572. (unsigned long)idx+1);
  573. return PAM_BUF_ERR;
  574. }
  575. }
  576. strcpy(*value, tmp);
  577. memset(tmp, '\0', sizeof(tmp));
  578. D(("Exit."));
  579. return PAM_SUCCESS;
  580. }
  581. static const char * _pam_get_item_byname(pam_handle_t *pamh, const char *name)
  582. {
  583. /*
  584. * This function just allows me to use names as given in the config
  585. * file and translate them into the appropriate PAM_ITEM macro
  586. */
  587. int item;
  588. const void *itemval;
  589. D(("Called."));
  590. if (strcmp(name, "PAM_USER") == 0 || strcmp(name, "HOME") == 0 || strcmp(name, "SHELL") == 0) {
  591. item = PAM_USER;
  592. } else if (strcmp(name, "PAM_USER_PROMPT") == 0) {
  593. item = PAM_USER_PROMPT;
  594. } else if (strcmp(name, "PAM_TTY") == 0) {
  595. item = PAM_TTY;
  596. } else if (strcmp(name, "PAM_RUSER") == 0) {
  597. item = PAM_RUSER;
  598. } else if (strcmp(name, "PAM_RHOST") == 0) {
  599. item = PAM_RHOST;
  600. } else {
  601. D(("Unknown PAM_ITEM: <%s>", name));
  602. pam_syslog (pamh, LOG_ERR, "Unknown PAM_ITEM: <%s>", name);
  603. return NULL;
  604. }
  605. if (pam_get_item(pamh, item, &itemval) != PAM_SUCCESS) {
  606. D(("pam_get_item failed"));
  607. return NULL; /* let pam_get_item() log the error */
  608. }
  609. if (itemval && (strcmp(name, "HOME") == 0 || strcmp(name, "SHELL") == 0)) {
  610. struct passwd *user_entry;
  611. user_entry = pam_modutil_getpwnam (pamh, itemval);
  612. if (!user_entry) {
  613. pam_syslog(pamh, LOG_ERR, "No such user!?");
  614. return NULL;
  615. }
  616. return (strcmp(name, "SHELL") == 0) ?
  617. user_entry->pw_shell :
  618. user_entry->pw_dir;
  619. }
  620. D(("Exit."));
  621. return itemval;
  622. }
  623. static int _define_var(pam_handle_t *pamh, int ctrl, VAR *var)
  624. {
  625. /* We have a variable to define, this is a simple function */
  626. char *envvar;
  627. int retval = PAM_SUCCESS;
  628. D(("Called."));
  629. if (asprintf(&envvar, "%s=%s", var->name, var->value) < 0) {
  630. pam_syslog(pamh, LOG_CRIT, "out of memory");
  631. return PAM_BUF_ERR;
  632. }
  633. retval = pam_putenv(pamh, envvar);
  634. if (ctrl & PAM_DEBUG_ARG) {
  635. pam_syslog(pamh, LOG_DEBUG, "pam_putenv(\"%s\")", envvar);
  636. }
  637. _pam_drop(envvar);
  638. D(("Exit."));
  639. return retval;
  640. }
  641. static int _undefine_var(pam_handle_t *pamh, int ctrl, VAR *var)
  642. {
  643. /* We have a variable to undefine, this is a simple function */
  644. D(("Called and exit."));
  645. if (ctrl & PAM_DEBUG_ARG) {
  646. pam_syslog(pamh, LOG_DEBUG, "remove variable \"%s\"", var->name);
  647. }
  648. return pam_putenv(pamh, var->name);
  649. }
  650. static void _clean_var(VAR *var)
  651. {
  652. if (var->name) {
  653. free(var->name);
  654. }
  655. if (var->defval && (&quote != var->defval)) {
  656. free(var->defval);
  657. }
  658. if (var->override && (&quote != var->override)) {
  659. free(var->override);
  660. }
  661. var->name = NULL;
  662. var->value = NULL; /* never has memory specific to it */
  663. var->defval = NULL;
  664. var->override = NULL;
  665. return;
  666. }
  667. /* --- authentication management functions (only) --- */
  668. int
  669. pam_sm_authenticate (pam_handle_t *pamh UNUSED, int flags UNUSED,
  670. int argc UNUSED, const char **argv UNUSED)
  671. {
  672. return PAM_IGNORE;
  673. }
  674. static int
  675. handle_env (pam_handle_t *pamh, int argc, const char **argv)
  676. {
  677. int retval, ctrl, readenv=DEFAULT_READ_ENVFILE;
  678. int user_readenv = DEFAULT_USER_READ_ENVFILE;
  679. const char *conf_file = NULL, *env_file = NULL, *user_env_file = NULL;
  680. /*
  681. * this module sets environment variables read in from a file
  682. */
  683. D(("Called."));
  684. ctrl = _pam_parse(pamh, argc, argv, &conf_file, &env_file,
  685. &readenv, &user_env_file, &user_readenv);
  686. retval = _parse_config_file(pamh, ctrl, conf_file);
  687. if(readenv && retval == PAM_SUCCESS) {
  688. retval = _parse_env_file(pamh, ctrl, env_file);
  689. if (retval == PAM_IGNORE)
  690. retval = PAM_SUCCESS;
  691. }
  692. if(user_readenv && retval == PAM_SUCCESS) {
  693. char *envpath = NULL;
  694. struct passwd *user_entry = NULL;
  695. const char *username;
  696. struct stat statbuf;
  697. username = _pam_get_item_byname(pamh, "PAM_USER");
  698. if (username)
  699. user_entry = pam_modutil_getpwnam (pamh, username);
  700. if (!user_entry) {
  701. pam_syslog(pamh, LOG_ERR, "No such user!?");
  702. }
  703. else {
  704. if (asprintf(&envpath, "%s/%s", user_entry->pw_dir, user_env_file) < 0)
  705. {
  706. pam_syslog(pamh, LOG_CRIT, "Out of memory");
  707. return PAM_BUF_ERR;
  708. }
  709. if (stat(envpath, &statbuf) == 0) {
  710. PAM_MODUTIL_DEF_PRIVS(privs);
  711. if (pam_modutil_drop_priv(pamh, &privs, user_entry)) {
  712. retval = PAM_SESSION_ERR;
  713. } else {
  714. retval = _parse_config_file(pamh, ctrl, envpath);
  715. if (pam_modutil_regain_priv(pamh, &privs))
  716. retval = PAM_SESSION_ERR;
  717. }
  718. if (retval == PAM_IGNORE)
  719. retval = PAM_SUCCESS;
  720. }
  721. free(envpath);
  722. }
  723. }
  724. /* indicate success or failure */
  725. D(("Exit."));
  726. return retval;
  727. }
  728. int
  729. pam_sm_acct_mgmt (pam_handle_t *pamh UNUSED, int flags UNUSED,
  730. int argc UNUSED, const char **argv UNUSED)
  731. {
  732. pam_syslog (pamh, LOG_NOTICE, "pam_sm_acct_mgmt called inappropriately");
  733. return PAM_SERVICE_ERR;
  734. }
  735. int
  736. pam_sm_setcred (pam_handle_t *pamh, int flags UNUSED,
  737. int argc, const char **argv)
  738. {
  739. D(("Called"));
  740. return handle_env (pamh, argc, argv);
  741. }
  742. int
  743. pam_sm_open_session (pam_handle_t *pamh, int flags UNUSED,
  744. int argc, const char **argv)
  745. {
  746. D(("Called"));
  747. return handle_env (pamh, argc, argv);
  748. }
  749. int
  750. pam_sm_close_session (pam_handle_t *pamh UNUSED, int flags UNUSED,
  751. int argc UNUSED, const char **argv UNUSED)
  752. {
  753. D(("Called and Exit"));
  754. return PAM_SUCCESS;
  755. }
  756. int
  757. pam_sm_chauthtok (pam_handle_t *pamh UNUSED, int flags UNUSED,
  758. int argc UNUSED, const char **argv UNUSED)
  759. {
  760. pam_syslog (pamh, LOG_NOTICE, "pam_sm_chauthtok called inappropriately");
  761. return PAM_SERVICE_ERR;
  762. }
  763. /* end of module definition */