1 /*
2  *	pam_mount
3  *	Copyright (C) Elvis Pfützenreuter <epx@conectiva.com>, 2000
4  *	Copyright © Jan Engelhardt, 2005 - 2010
5  *	Copyright © Bastian Kleineidam, 2005
6  *
7  *	This file is part of pam_mount; you can redistribute it and/or
8  *	modify it under the terms of the GNU Lesser General Public License
9  *	as published by the Free Software Foundation; either version 2.1
10  *	of the License, or (at your option) any later version.
11  */
12 #define PAM_SM_ACCOUNT 1
13 #define PAM_SM_AUTH 1
14 #define PAM_SM_SESSION 1
15 #define PAM_SM_PASSWORD 1
16 
17 #include <security/pam_appl.h>
18 #include <security/pam_modules.h>
19 #include <sys/mman.h>
20 #include <sys/types.h>
21 #include <sys/wait.h>
22 #include <assert.h>
23 #include <errno.h>
24 #include <pthread.h>
25 #include <signal.h>
26 #include <stdbool.h>
27 #include <stdio.h>
28 #include <stdlib.h>
29 #include <string.h>
30 #include <unistd.h>
31 #include <libHX/defs.h>
32 #include <libHX/proc.h>
33 #include <libHX.h>
34 #include "pam_mount.h"
35 
36 #ifndef PAM_EXTERN
37 #	define PAM_EXTERN
38 #endif
39 
40 #if defined(__OpenBSD__) || defined(__NetBSD__) || defined(__APPLE__)
41 #	define CONFIGFILE "/etc/pam_mount.conf.xml"
42 #elif defined(__FreeBSD__) || defined(__DragonFly__)
43 #  define CONFIGFILE "/usr/local/etc/security/pam_mount.conf.xml"
44 #else
45 #	define CONFIGFILE "/etc/security/pam_mount.conf.xml"
46 #endif
47 
48 struct pam_args {
49 	bool get_pw_from_pam, get_pw_interactive, propagate_pw;
50 };
51 
52 /* Functions */
53 static void clean_config(pam_handle_t *, void *, int);
54 static int converse(pam_handle_t *, int, const struct pam_message **,
55 	struct pam_response **);
56 static int modify_pm_count(struct config *, char *, char *);
57 static void parse_pam_args(int, const char **);
58 static int read_password(pam_handle_t *, const char *, char **);
59 
60 /* Variables */
61 static const char *envpath_saved;
62 struct config Config;
63 struct pam_args Args;
64 
65 //-----------------------------------------------------------------------------
66 /**
67  * parse_pam_args -
68  * @argv:	NULL-terminated argument vector
69  * @argc:	number of elements in @argc
70  *
71  * Global @Args is initialized, based on @argv.
72  */
parse_pam_args(int argc,const char ** argv)73 static void parse_pam_args(int argc, const char **argv)
74 {
75 	int i;
76 
77 	assert(argc >= 0);
78 	for (i = 0; i < argc; i++)
79 		assert(argv[i] != NULL);
80 
81 	/* first, set default values */
82 	Args.get_pw_from_pam    = true;
83 	Args.get_pw_interactive = true;
84 	Args.propagate_pw       = true;
85 
86 	for (i = 0; i < argc; ++i) {
87 		if (strcasecmp("enable_pam_password", argv[i]) == 0)
88 			Args.get_pw_from_pam = true;
89 		else if (strcasecmp("disable_pam_password", argv[i]) == 0)
90 			Args.get_pw_from_pam = false;
91 		else if (strcasecmp("enable_interactive", argv[i]) == 0)
92 			Args.get_pw_interactive = true;
93 		else if (strcasecmp("disable_interactive", argv[i]) == 0)
94 			Args.get_pw_interactive = false;
95 		else if (strcasecmp("enable_propagate_password", argv[i]) == 0)
96 			Args.propagate_pw = true;
97 		else if (strcasecmp("disable_propagate_password", argv[i]) == 0)
98 			Args.propagate_pw = false;
99 		else if (strcasecmp("debug", argv[i]) == 0)
100 			Debug = true;
101 		else
102 			w4rn("unknown pam_mount option \"%s\"\n", argv[i]);
103 	}
104 }
105 
106 /**
107  * clean_config -
108  * @pamh:	PAM handle
109  * @data:	custom data pointer
110  * @err:
111  *
112  * Free data from a struct config variable.
113  * Note: This is registered as a PAM callback function and is called directly.
114  */
clean_config(pam_handle_t * pamh,void * data,int err)115 static void clean_config(pam_handle_t *pamh, void *data, int err)
116 {
117 	w4rn("Clean global config (%d)\n", err);
118 	freeconfig(data);
119 }
120 
121 /**
122  * clean_system_authtok -
123  * @pamh:	PAM handle
124  * @data:	custom data pointer
125  * @err:
126  *
127  * Zero and free @data if it is not %NULL.
128  * Note: This is registered as a PAM callback function and is called directly.
129  *
130  * FIXME: Not binary-password safe.
131  */
clean_system_authtok(pam_handle_t * pamh,void * data,int errcode)132 static void clean_system_authtok(pam_handle_t *pamh, void *data, int errcode)
133 {
134 	w4rn("clean system authtok=%p (%d)\n", data, errcode);
135 
136 	if (data != NULL) {
137 		unsigned int len = strlen(data) + 1;
138 		memset(data, 0, len);
139 		munlock(data, len);
140 		free(data);
141 	}
142 }
143 
144 /**
145  * converse -
146  * @pamh:	PAM handle
147  * @nargs:	number of messages
148  * @message:	PAM message array
149  * @resp:	user response array
150  *
151  * Note: Adapted from pam_unix/support.c.
152  */
converse(pam_handle_t * pamh,int nargs,const struct pam_message ** message,struct pam_response ** resp)153 static int converse(pam_handle_t *pamh, int nargs,
154     const struct pam_message **message, struct pam_response **resp)
155 {
156 	int retval;
157 	struct pam_conv *conv;
158 
159 	assert(pamh != NULL);
160 	assert(nargs >= 0);
161 	assert(resp != NULL);
162 
163 	*resp = NULL;
164 	retval = pam_get_item(pamh, PAM_CONV, static_cast(const void **,
165 	         static_cast(void *, &conv)));
166 
167 	if (retval != PAM_SUCCESS) {
168 		l0g("pam_get_item: %s\n", pam_strerror(pamh, retval));
169 	} else if (conv == NULL || conv->conv == NULL) {
170 		w4rn("No converse function available\n");
171 	} else {
172 		retval = conv->conv(nargs, message, resp, conv->appdata_ptr);
173 		if (retval != PAM_SUCCESS)
174 			l0g("conv->conv(...): %s\n", pam_strerror(pamh, retval));
175 	}
176 
177 	if (resp == NULL || *resp == NULL || (*resp)->resp == NULL)
178 		retval = PAM_AUTH_ERR;
179 
180 	assert(retval != PAM_SUCCESS || (resp != NULL && *resp != NULL &&
181 	       (*resp)->resp != NULL));
182 	return retval; /* propagate error status */
183 }
184 
185 /**
186  * read_password -
187  * @pamh:	PAM handle
188  * @prompt:	a prompt message
189  * @pass:	space for entered password
190  *
191  * Returns PAM error code or %PAM_SUCCESS.
192  * Note: Adapted from pam_unix/support.c:_unix_read_password().
193  */
read_password(pam_handle_t * pamh,const char * prompt,char ** pass)194 static int read_password(pam_handle_t *pamh, const char *prompt, char **pass)
195 {
196 	int retval;
197 	struct pam_message msg;
198 	const struct pam_message *pmsg = &msg;
199 	struct pam_response *resp = NULL;
200 
201 	assert(pamh != NULL);
202 	assert(pass != NULL);
203 
204 	*pass = NULL;
205 	msg.msg_style = PAM_PROMPT_ECHO_OFF;
206 	msg.msg       = (prompt == NULL) ? "Password: " : prompt;
207 	retval  = converse(pamh, 1, &pmsg, &resp);
208 	if (retval == PAM_SUCCESS)
209 		*pass = xstrdup(resp->resp);
210 
211 	assert(retval != PAM_SUCCESS || (pass != NULL && *pass != NULL));
212 	return retval;
213 }
214 
pmt_sigpipe_setup(bool block_it)215 static void pmt_sigpipe_setup(bool block_it)
216 {
217 	static pthread_mutex_t sp_lock = PTHREAD_MUTEX_INITIALIZER;
218 	static int sp_blocked = 0;
219 	static bool sp_previous;
220 	sigset_t set, oldset;
221 
222 	pthread_mutex_lock(&sp_lock);
223 	if (block_it) {
224 		if (++sp_blocked == 1) {
225 			sigemptyset(&set);
226 			sigaddset(&set, SIGPIPE);
227 			sigprocmask(SIG_BLOCK, &set, &oldset);
228 			sp_previous = sigismember(&oldset, SIGPIPE);
229 		}
230 	} else {
231 		if (--sp_blocked == 0 && sp_previous) {
232 			sigemptyset(&set);
233 			sigaddset(&set, SIGPIPE);
234 			sigtimedwait(&set, NULL, &(struct timespec){0, 0});
235 			sigprocmask(SIG_UNBLOCK, &set, NULL);
236 		}
237 	}
238 
239 	pthread_mutex_unlock(&sp_lock);
240 }
241 
common_init(pam_handle_t * pamh,int argc,const char ** argv)242 static int common_init(pam_handle_t *pamh, int argc, const char **argv)
243 {
244 	const char *pam_user;
245 	char buf[8];
246 	int ret;
247 
248 	pmtlog_prefix = "pam_mount";
249 	pmtlog_path[PMTLOG_ERR][PMTLOG_SYSLOG] = true;
250 	pmtlog_path[PMTLOG_ERR][PMTLOG_STDERR] = true;
251 	pmtlog_path[PMTLOG_DBG][PMTLOG_SYSLOG] = Debug;
252 	pmtlog_path[PMTLOG_DBG][PMTLOG_STDERR] = Debug;
253 
254 	ret = HX_init();
255 	if (ret <= 0)
256 		l0g("libHX init failed: %s\n", strerror(errno));
257 
258 	initconfig(&Config);
259 	parse_pam_args(argc, argv);
260 	/*
261 	 * call pam_get_user again because ssh calls PAM fns from seperate
262  	 * processes.
263 	 */
264 	ret = pam_get_user(pamh, &pam_user, NULL);
265 	if (ret != PAM_SUCCESS) {
266 		l0g("could not get user");
267 		/*
268 		 * do NOT return %PAM_SERVICE_ERR or root will not be able to
269 		 * su to other users.
270 		 * Also, if we could not get the user's info, an earlier auth
271 		 * module (like pam_unix2) likely blocked login already.
272 		 */
273 		return PAM_SUCCESS;
274 	}
275 	/*
276 	 * FIXME: free me! the dup is requried because result of pam_get_user()
277 	 * disappears (valgrind)
278 	 */
279 	Config.user = relookup_user(pam_user);
280 	if (!readconfig(CONFIGFILE, true, &Config))
281 		return PAM_SERVICE_ERR;
282 
283 	/* reinitialize after @Debug may have changed */
284 	pmtlog_path[PMTLOG_DBG][PMTLOG_STDERR] = Debug;
285 	pmtlog_path[PMTLOG_DBG][PMTLOG_SYSLOG] = Debug;
286 
287 	snprintf(buf, sizeof(buf), "%u", Debug);
288 	setenv("_PMT_DEBUG_LEVEL", buf, true);
289 
290 	pmt_sigpipe_setup(true);
291 	return -1;
292 }
293 
common_exit(void)294 static void common_exit(void)
295 {
296 	pmt_sigpipe_setup(false);
297 	HX_exit();
298 }
299 
auth_grab_authtok(pam_handle_t * pamh,struct config * config)300 static void auth_grab_authtok(pam_handle_t *pamh, struct config *config)
301 {
302 	char *authtok = NULL;
303 	int ret;
304 
305 	if (Args.get_pw_from_pam) {
306 		char *ptr = NULL;
307 
308 		ret = pam_get_item(pamh, PAM_AUTHTOK, static_cast(const void **,
309 		      static_cast(void *, &ptr)));
310 		if (ret == PAM_SUCCESS && ptr != NULL)
311 			authtok = xstrdup(ptr);
312 	}
313 	if (authtok == NULL && Args.get_pw_interactive) {
314 		ret = read_password(pamh, config->msg_authpw, &authtok);
315 		if (ret == PAM_SUCCESS && Args.propagate_pw) {
316 			/*
317 			 * pam_set_item() copies to PAM-internal memory.
318 			 *
319 			 * Using pam_set_item(PAM_AUTHTOK) here to make the
320 			 * password that was just entered available to further
321 			 * PAM modules.
322 			 */
323 			ret = pam_set_item(pamh, PAM_AUTHTOK, authtok);
324 			if (ret != PAM_SUCCESS)
325 				l0g("warning: failure to export password (%s)\n",
326 				    pam_strerror(pamh, ret));
327 		}
328 	}
329 
330 	/*
331 	 * Save auth token for pam_mount itself, since PAM_AUTHTOK
332 	 * will be gone when the auth stage exits.
333 	 */
334 	if (authtok != NULL) {
335 		ret = pam_set_data(pamh, "pam_mount_system_authtok", authtok,
336 		                   clean_system_authtok);
337 		if (ret == PAM_SUCCESS) {
338 			if (mlock(authtok, strlen(authtok) + 1) < 0)
339 				w4rn("mlock authtok: %s\n", strerror(errno));
340 		} else {
341 			l0g("error trying to save authtok for session code\n");
342 		}
343 	}
344 }
345 
346 /**
347  * pam_sm_authenticate -
348  * @pamh:	PAM handle
349  * @flags:	PAM flags
350  * @argc:	number of elements in @argv
351  * @argv:	NULL-terminated argument vector
352  *
353  * Called by the PAM layer. The user's system password is added to PAM's
354  * global module data. This is because pam_sm_open_session() does not allow
355  * access to the user's password. Returns the PAM error code or %PAM_SUCCESS.
356  */
pam_sm_authenticate(pam_handle_t * pamh,int flags,int argc,const char ** argv)357 PAM_EXTERN EXPORT_SYMBOL int pam_sm_authenticate(pam_handle_t *pamh, int flags,
358     int argc, const char **argv)
359 {
360 	int ret = PAM_SUCCESS;
361 
362 	assert(pamh != NULL);
363 
364 	if ((ret = common_init(pamh, argc, argv)) != -1)
365 		return ret;
366 	w4rn(PACKAGE_STRING ": entering auth stage\n");
367 	auth_grab_authtok(pamh, &Config);
368 	common_exit();
369 	/*
370 	 * pam_mount is not really meant to be an auth module. So we should not
371 	 * hinder the login process.
372 	 */
373 	return PAM_SUCCESS;
374 }
375 
376 /**
377  * On login, $PATH is correctly set to ENV_ROOTPATH (from /etc/login.defs),
378  * while on logout, it happens to be ENV_PATH only. This is problematic,
379  * since some programs are in /sbin and /usr/sbin which is
380  * often not contained in ENV_PATH.
381  *
382  * In short: Another workaround for coreutils.
383  */
envpath_init(const char * new_path)384 static void envpath_init(const char *new_path)
385 {
386 	envpath_saved = getenv("PATH");
387 	setenv("PATH", new_path, true);
388 }
389 
envpath_restore(void)390 static void envpath_restore(void)
391 {
392 	if (envpath_saved == NULL)
393 		unsetenv("PATH");
394 	else
395 		setenv("PATH", envpath_saved, true);
396 }
397 
398 /**
399  * modify_pm_count -
400  * @config:
401  * @user:
402  * @operation:	string specifying numerical increment
403  *
404  * Calls out to the `pmvarrun` helper utility to adjust the mount reference
405  * count in /var/run/pam_mount/@user for the specified user.
406  * Returns the new reference count value on success, or -1 on error.
407  *
408  * Note: Modified version of pam_console.c:use_count()
409  */
modify_pm_count(struct config * config,char * user,char * operation)410 static int modify_pm_count(struct config *config, char *user,
411     char *operation)
412 {
413 	FILE *fp = NULL;
414 	struct HXformat_map *vinfo;
415 	struct HXdeque *argv;
416 	struct HXproc proc;
417 	int ret = -1, use_count;
418 
419 	assert(user != NULL);
420 	assert(operation != NULL);
421 
422 	if ((vinfo = HXformat_init()) == NULL)
423 		goto out;
424 	format_add(vinfo, "USER", user);
425 	format_add(vinfo, "OPERATION", operation);
426 	misc_add_ntdom(vinfo, user);
427 
428 	argv = arglist_build(config->command[CMD_PMVARRUN], vinfo);
429 	memset(&proc, 0, sizeof(proc));
430 	proc.p_flags = HXPROC_VERBOSE | HXPROC_STDOUT;
431 	proc.p_ops   = &pmt_dropprivs_ops;
432 	if ((ret = pmt_spawn_dq(argv, &proc)) <= 0) {
433 		l0g("error executing pmvarrun: %s\n", strerror(-ret));
434 		goto out;
435 	}
436 	ret = -1;
437 	if ((fp = fdopen(proc.p_stdout, "r")) == NULL)
438 		goto out2;
439 	if (fscanf(fp, "%d", &use_count) != 1)
440 		w4rn("error reading login count from pmvarrun\n");
441 	else
442 		w4rn("pmvarrun says login count is %d\n", use_count);
443  out2:
444 	if (fp != NULL)
445 		fclose(fp);
446 	else
447 		close(proc.p_stdout);
448 	if (HXproc_wait(&proc) >= 0 && proc.p_exited && proc.p_status == 0)
449 		ret = use_count;
450  out:
451 	if (vinfo != NULL)
452 		HXformat_free(vinfo);
453 	return ret;
454 }
455 
456 /**
457  * ses_grab_authtok - get the password from PAM
458  *
459  * Session stage: reretrieve password that the auth stage stored.
460  * If that does not work, use interactive prompting if enabled.
461  */
ses_grab_authtok(pam_handle_t * pamh)462 static char *ses_grab_authtok(pam_handle_t *pamh)
463 {
464 	char *authtok = NULL;
465 	int ret;
466 
467 	ret = pam_get_data(pamh, "pam_mount_system_authtok",
468 	      static_cast(const void **, static_cast(void *, &authtok)));
469 	if (ret == PAM_SUCCESS)
470 		return authtok;
471 
472 	/* No stored password, get one, if allowed to. */
473 	if (Args.get_pw_interactive) {
474 		ret = read_password(pamh, Config.msg_sessionpw, &authtok);
475 		if (ret != PAM_SUCCESS)
476 			/* authtok is %NULL now */
477 			l0g("warning: could not obtain password "
478 			    "interactively either\n");
479 	}
480 	if (authtok != NULL) {
481 		ret = pam_set_data(pamh, "pam_mount_system_authtok",
482 		      authtok, clean_system_authtok);
483 		if (ret == PAM_SUCCESS) {
484 			if (mlock(authtok, strlen(authtok) + 1) < 0)
485 				w4rn("mlock authtok: %s\n", strerror(errno));
486 		} else {
487 			l0g("error trying to save authtok for session code\n");
488 		}
489 	}
490 	/*
491 	 * Always proceed, even if there is no password. Some volumes may not
492 	 * need one, e.g. bind mounts and networked/unencrypted volumes.
493 	 */
494 	return authtok;
495 }
496 
process_volumes(struct config * config,const char * authtok)497 static int process_volumes(struct config *config, const char *authtok)
498 {
499 	int ret = PAM_SUCCESS;
500 	struct vol *vol;
501 
502 	HXlist_for_each_entry(vol, &config->volume_list, list) {
503 		/*
504 		 * Remember what we processed already - the function can
505 		 * be called multiple times.
506 		 */
507 		if (vol->mnt_processed)
508 			continue;
509 		vol->mnt_processed = true;
510 		/*
511 		 * luserconf_volume_record_sane() is called here so that a user
512 		 * can nest loopback images. otherwise ownership tests will
513 		 * fail if parent loopback image not yet mounted.
514 		 * volume_record_sane() is here to be consistent.
515 		 */
516 		if (!volume_record_sane(config, vol))
517 			continue;
518 		if (!vol->globalconf &&
519 		    !luserconf_volume_record_sane(config, vol))
520 			continue;
521 
522 		if (!mount_op(do_mount, config, vol, authtok)) {
523 			l0g("mount of %s failed\n", znul(vol->volume));
524 			ret = PAM_SERVICE_ERR;
525 		}
526 	}
527 	return ret;
528 }
529 
530 /**
531  * pam_sm_open_session -
532  * @pamh:	PAM handle
533  * @flags:	PAM flags
534  * @argc:	number of elements in @argv
535  * @argv:	NULL-terminated argument vector
536  *
537  * Entrypoint from the PAM layer. Starts the wheels and eventually mounts the
538  * user's directories according to pam_mount.conf.xml. Returns the PAM error
539  * code or %PAM_SUCCESS.
540  */
pam_sm_open_session(pam_handle_t * pamh,int flags,int argc,const char ** argv)541 PAM_EXTERN EXPORT_SYMBOL int pam_sm_open_session(pam_handle_t *pamh, int flags,
542     int argc, const char **argv)
543 {
544 	int ret;
545 	const char *krb5;
546 	char *system_authtok = NULL;
547 	const void *tmp;
548 	int getval;
549 
550 	assert(pamh != NULL);
551 
552 	if ((ret = common_init(pamh, argc, argv)) != -1)
553 		return ret;
554 
555 	w4rn(PACKAGE_STRING ": entering session stage\n");
556 
557 	/*
558 	 * Environment variables set with setenv() only last while PAM is
559 	 * active, i.e. disappear when the shell is started. On the other hand,
560 	 * variabled fed to pam_putenv() are only visible once the shell
561 	 * started.
562 	 */
563 	/*
564 	 * Get the Kerberos CCNAME so we can make it available to the
565 	 * mount command later on.
566 	 */
567 	krb5 = pam_getenv(pamh, "KRB5CCNAME");
568 	if (krb5 != NULL && setenv("KRB5CCNAME", krb5, true) < 0)
569 		l0g("KRB5CCNAME setenv failed\n");
570 
571 	/* Store initialized config as PAM data */
572 	getval = pam_get_data(pamh, "pam_mount_config", &tmp);
573 	if (getval == PAM_NO_MODULE_DATA) {
574 		ret = pam_set_data(pamh, "pam_mount_config",
575 		      &Config, clean_config);
576 		if (ret != PAM_SUCCESS) {
577 			l0g("error trying to save config structure\n");
578 			goto out;
579 		}
580 		/* Up the reference count by one, for freeconfig */
581 		HX_init();
582 	}
583 
584 	if (!expandconfig(&Config)) {
585 		l0g("error expanding configuration\n");
586 		ret = PAM_SERVICE_ERR;
587 		goto out;
588 	}
589 	if (Config.volume_list.items > 0)
590 		/* There are some volumes, so grab a password. */
591 		system_authtok = ses_grab_authtok(pamh);
592 
593 	misc_dump_id("Session open");
594 	envpath_init(Config.path);
595 	ret = process_volumes(&Config, system_authtok);
596 
597 	/*
598 	 * Read luserconf after mounting of initial volumes. This makes it
599 	 * possible to store luserconfs on net volumes themselves.
600 	 */
601 	if (Config.luserconf != NULL && *Config.luserconf != '\0' &&
602 	    pmt_fileop_exists(Config.luserconf)) {
603 		w4rn("going to readconfig %s\n", Config.luserconf);
604 		if (!pmt_fileop_owns(Config.user, Config.luserconf)) {
605 			w4rn("%s does not exist or is not owned by user\n",
606 			     Config.luserconf);
607 		} else if (!readconfig(Config.luserconf, false, &Config)) {
608 			ret = PAM_SERVICE_ERR;
609 		} else if (!expandconfig(&Config)) {
610 			ret = PAM_SERVICE_ERR;
611 			l0g("error expanding configuration\n");
612 		}
613 	}
614 
615 	if (Config.volume_list.items == 0) {
616 		w4rn("no volumes to mount\n");
617 		ret = PAM_SUCCESS;
618 	} else {
619 		/*
620 		 * If there are no global volumes, but luserconf volumes,
621 		 * and we still have no password, ask for one now.
622 		 */
623 		if (system_authtok == NULL)
624 			system_authtok = ses_grab_authtok(pamh);
625 		ret = process_volumes(&Config, system_authtok);
626 	}
627 
628 	modify_pm_count(&Config, Config.user, "1");
629 	envpath_restore();
630 	if (getuid() == 0)
631 		/* Make sure root can always log in. */
632 		/* NB: I don't even wanna think of SELINUX's ambiguous UIDs... */
633 		ret = PAM_SUCCESS;
634 
635 	/*
636 	 * If mounting something failed, e.g. ret = %PAM_SERVICE_ERR, we have
637 	 * to unravel everything and umount all volumes. But *only* if
638 	 * pam_mount was configured as a "required" module. How can this info
639 	 * be obtained?
640 	 * For now, always assume "optional", so that the volumes are
641 	 * definitely unmounted when the user logs out again.
642 	 */
643 	ret = PAM_SUCCESS;
644  out:
645 	if (krb5 != NULL)
646 		unsetenv("KRB5CCNAME");
647 	w4rn("done opening session (ret=%d)\n", ret);
648 	common_exit();
649 	return ret;
650 }
651 
652 /**
653  * pam_sm_chauthtok -
654  * @pamh:	PAM handle
655  * @flags:	PAM flags
656  * @argc:	number of elements in @argv
657  * @argv:	NULL-terminated argument vector
658  *
659  * This is a placeholder function so PAM does not get mad.
660  */
pam_sm_chauthtok(pam_handle_t * pamh,int flags,int argc,const char ** argv)661 PAM_EXTERN EXPORT_SYMBOL int pam_sm_chauthtok(pam_handle_t *pamh, int flags,
662     int argc, const char **argv)
663 {
664 	return pam_sm_authenticate(pamh, flags, argc, argv);
665 }
666 
667 /**
668  * pam_sm_close_session -
669  * @pamh:	PAM handle
670  * @flags:	PAM flags
671  * @argc:	number of elements in @argv
672  * @argv:	NULL-terminated argument vector
673  *
674  * Entrypoint from the PAM layer. Stops all wheels and eventually unmounts the
675  * user's directories. Returns the PAM error code or %PAM_SUCCESS.
676  *
677  * FIXME: This function currently always returns %PAM_SUCCESS. Should it
678  * return soemthing else when errors occur and all unmounts have been
679  * attempted?
680  */
pam_sm_close_session(pam_handle_t * pamh,int flags,int argc,const char ** argv)681 PAM_EXTERN EXPORT_SYMBOL int pam_sm_close_session(pam_handle_t *pamh,
682     int flags, int argc, const char **argv)
683 {
684 	const char *pam_user = NULL;
685 	int ret;
686 
687 	assert(pamh != NULL);
688 
689 	ret = HX_init();
690 	if (ret <= 0)
691 		l0g("libHX init failed: %s\n", strerror(errno));
692 	ret = PAM_SUCCESS;
693 	w4rn("received order to close things\n");
694 	if (Config.volume_list.items == 0) {
695 		w4rn("No volumes to umount\n");
696 		goto out;
697 	}
698 
699 	misc_dump_id("Session close");
700 	/*
701 	 * call pam_get_user() again because ssh calls PAM fns from seperate
702  	 * processes.
703 	 */
704 	ret = pam_get_user(pamh, &pam_user, NULL);
705 	if (ret != PAM_SUCCESS) {
706 		l0g("could not get user\n");
707 		goto out;
708 	}
709 	/*
710 	 * FIXME: free me! the dup is requried because result of pam_get_user
711 	 * disappears (valgrind)
712 	 */
713 	Config.user = relookup_user(pam_user);
714 	/* if our CWD is in the home directory, it might not get umounted */
715 	if (chdir("/") != 0)
716 		l0g("could not chdir\n");
717 
718  out:
719 	envpath_init(Config.path);
720 	if (modify_pm_count(&Config, Config.user, "-1") > 0)
721 		w4rn("%s seems to have other remaining open sessions\n",
722 		     Config.user);
723 	else
724 		umount_final(&Config);
725 
726 	envpath_restore();
727 	/*
728 	 * Note that PMConfig is automatically freed later in clean_config()
729 	 */
730 	w4rn("pam_mount execution complete\n");
731 	HX_exit();
732 	return ret;
733 }
734 
735 /**
736  * pam_sm_setcred -
737  * @pamh:	PAM handle
738  * @flags:	PAM flags
739  * @argc:	number of elements in @argv
740  * @argv:	NULL-terminated argument vector
741  *
742  * This is a placeholder function so PAM does not get mad.
743  */
pam_sm_setcred(pam_handle_t * pamh,int flags,int argc,const char ** argv)744 PAM_EXTERN EXPORT_SYMBOL int pam_sm_setcred(pam_handle_t *pamh, int flags,
745     int argc, const char **argv)
746 {
747 	return PAM_SUCCESS;
748 }
749 
750 /**
751  * pam_sm_acct_mgmt -
752  * @pamh:	PAM handle
753  * @flags:	PAM flags
754  * @argc:	number of elements in @argv
755  * @argv:	NULL-terminated argument vector
756  *
757  * This is a placeholder function so PAM does not get mad.
758  */
pam_sm_acct_mgmt(pam_handle_t * pamh,int flags,int argc,const char ** argv)759 PAM_EXTERN EXPORT_SYMBOL int pam_sm_acct_mgmt(pam_handle_t *pamh, int flags,
760     int argc, const char **argv)
761 {
762 	return PAM_IGNORE;
763 }
764 
765 #ifdef PAM_STATIC
766 /* static module data */
767 
768 EXPORT_SYMBOL struct pam_module _pam_mount_modstruct = {
769 	.name                 = "pam_mount",
770 	.pam_sm_authenticate  = pam_sm_authenticate,
771 	.pam_sm_setcred       = pam_sm_setcred,
772 	.pam_sm_acct_mgmt     = pam_sm_acct_mgmt,
773 	.pam_sm_open_sesion   = pam_sm_open_session,
774 	.pam_sm_close_session = pam_sm_close_session,
775 	.pam_sm_chauthtok     = pam_sm_chauthtok,
776 };
777 
778 #endif
779