1 /* This file is part of pam-modules.
2    Copyright (C) 2005-2008, 2010-2015, 2018 Sergey Poznyakoff
3 
4    This program is free software; you can redistribute it and/or modify it
5    under the terms of the GNU General Public License as published by the
6    Free Software Foundation; either version 3 of the License, or (at your
7    option) any later version.
8 
9    This program is distributed in the hope that it will be useful,
10    but WITHOUT ANY WARRANTY; without even the implied warranty of
11    MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
12    GNU General Public License for more details.
13 
14    You should have received a copy of the GNU General Public License along
15    with this program.  If not, see <http://www.gnu.org/licenses/>. */
16 
17 #ifdef HAVE__PAM_ACONF_H
18 # include <security/_pam_aconf.h>
19 #endif
20 #ifndef LINUX_PAM
21 # include <security/pam_appl.h>
22 #endif				/* LINUX_PAM */
23 #include <security/pam_modules.h>
24 
25 #include <sys/stat.h>
26 #include <sys/types.h>
27 #include <dirent.h>
28 #include <unistd.h>
29 #include <fcntl.h>
30 #include <stdio.h>
31 #include <stdlib.h>
32 #include <unistd.h>
33 #include <time.h>
34 #include <sys/time.h>
35 #include <sys/wait.h>
36 #include <ldap.h>
37 #include <pwd.h>
38 #include <grp.h>
39 
40 #include "graypam.h"
41 
42 /* indicate the following groups are defined */
43 #define PAM_SM_AUTH
44 
45 static long debug_level;
46 static int cntl_flags;
47 static char *config_file_name;
48 static int ldap_debug_level;
49 /* FIXME: This should be read from sshd_config */
50 static char *authorized_keys_file=".ssh/authorized_keys";
51 static char *ldap_config_name = "/etc/ldap.conf";
52 
53 struct pam_opt pam_opt[] = {
54 	{ PAM_OPTSTR(debug),  pam_opt_long, &debug_level },
55 	{ PAM_OPTSTR(debug),  pam_opt_const, &debug_level, { 1 } },
56 	{ PAM_OPTSTR(audit), pam_opt_const, &debug_level, { 100 } },
57 	{ PAM_OPTSTR(waitdebug), pam_opt_null, NULL, { 0 },
58 	  gray_wait_debug_fun },
59 	{ PAM_OPTSTR(config), pam_opt_string, &config_file_name },
60 	{ NULL }
61 };
62 
63 static void
_pam_parse(pam_handle_t * pamh,int argc,const char ** argv)64 _pam_parse(pam_handle_t *pamh, int argc, const char **argv)
65 {
66 	cntl_flags = 0;
67 	debug_level = 0;
68 	config_file_name = SYSCONFDIR "/" MODULE_NAME ".conf";
69 	gray_log_init(0, MODULE_NAME, LOG_AUTHPRIV);
70 	gray_parseopt(pam_opt, argc, argv);
71 }
72 
73 static void
argcv_free(int wc,char ** wv)74 argcv_free(int wc, char **wv)
75 {
76 	int i;
77 
78 	for (i = 0; i < wc; i++)
79 		free(wv[i]);
80 	free(wv);
81 }
82 
83 static void
argcvz_free(char ** wv)84 argcvz_free(char **wv)
85 {
86 	int i;
87 
88 	for (i = 0; wv[i]; i++)
89 		free(wv[i]);
90 	free(wv);
91 }
92 
93 static int
argcv_split(const char * str,int * pargc,char *** pargv)94 argcv_split(const char *str, int *pargc, char ***pargv)
95 {
96 	int argc, i;
97 	char **argv;
98 	const char *p;
99 	int rc = 0;
100 
101 	argc = 1;
102 	for (p = str; *p; p++) {
103 		if (*p == ' ')
104 			argc++;
105 	}
106 	argv = calloc(argc + 1, sizeof(argv[0]));
107 	if (!argv)
108 		return 1;
109 	for (i = 0, p = str;;) {
110 		size_t len = strcspn(p, " ");
111 		char *q = malloc(len + 1);
112 
113 		if (!q) {
114 			rc = errno;
115 			break;
116 		}
117 		memcpy(q, p, len);
118 		q[len] = 0;
119 		argv[i++] = q;
120 		p += len;
121 		if (p)
122 			p += strspn(p, " ");
123 		if (!*p)
124 			break;
125 	}
126 
127 	if (rc) {
128 		argcv_free(argc, argv);
129 		errno = rc;
130 		return 1;
131 	}
132 
133 	argv[i] = NULL;
134 	*pargc = argc;
135 	*pargv = argv;
136 	return 0;
137 }
138 
139 static char *
argcv_concat(int wc,char ** wv)140 argcv_concat(int wc, char **wv)
141 {
142 	char *res, *p;
143 	size_t size = 0;
144 	int i;
145 
146 	for (i = 0; i < wc; i++)
147 		size += strlen(wv[i]) + 1;
148 	res = malloc(size);
149 	if (!res)
150 		return 0;
151 	for (p = res, i = 0;;) {
152 		strcpy(p, wv[i]);
153 		p += strlen(wv[i]);
154 		if (++i < wc)
155 			*p++ = ' ';
156 		else
157 			break;
158 	}
159 	*p = 0;
160 	return res;
161 }
162 
163 static int
get_intval(struct gray_env * env,const char * name,int base,unsigned long * pv)164 get_intval(struct gray_env *env, const char *name, int base, unsigned long *pv)
165 {
166 	char *p;
167 	char *v = gray_env_get(env, name);
168 
169 	if (!v)
170 		return 1;
171 	*pv = strtoul(v, &p, base);
172 	if (*p) {
173 		_pam_log(LOG_ERR, "configuration variable %s is not integer",
174 			 name);
175 		return -1;
176 	}
177 	return 0;
178 }
179 
180 char *
parse_ldap_uri(const char * uri)181 parse_ldap_uri(const char *uri)
182 {
183 	int wc;
184 	char **wv;
185 	LDAPURLDesc *ludlist, **ludp;
186 	char **urls = NULL;
187 	int nurls = 0;
188 	char *ldapuri = NULL;
189 	int rc;
190 
191 	rc = ldap_url_parse(uri, &ludlist);
192 	if (rc != LDAP_URL_SUCCESS) {
193 		_pam_log(LOG_ERR, "cannot parse LDAP URL(s)=%s (%d)",
194 			 uri, rc);
195 		return NULL;
196 	}
197 
198 	for (ludp = &ludlist; *ludp; ) {
199 		LDAPURLDesc *lud = *ludp;
200 		char **tmp;
201 
202 		if (lud->lud_dn && lud->lud_dn[0]
203 		    && (lud->lud_host == NULL || lud->lud_host[0] == '\0'))  {
204 			/* if no host but a DN is provided, try
205 			   DNS SRV to gather the host list */
206 			char *domain = NULL, *hostlist = NULL;
207 			size_t i;
208 
209 			if (ldap_dn2domain(lud->lud_dn, &domain) ||
210 			    !domain) {
211 				_pam_log(LOG_ERR,
212 					 "DNS SRV: cannot convert "
213 					 "DN=\"%s\" into a domain",
214 					 lud->lud_dn);
215 				goto dnssrv_free;
216 			}
217 
218 			rc = ldap_domain2hostlist(domain, &hostlist);
219 			if (rc) {
220 				_pam_log(LOG_ERR,
221 					 "DNS SRV: cannot convert "
222 					 "domain=%s into a hostlist",
223 					 domain);
224 				goto dnssrv_free;
225 			}
226 
227 			if (argcv_split(hostlist, &wc, &wv)) {
228 				_pam_log(LOG_ERR,
229 					 "DNS SRV: could not parse "
230 					 "hostlist=\"%s\": %s",
231 					 hostlist, strerror(errno));
232 				goto dnssrv_free;
233 			}
234 
235 			tmp = realloc(urls, sizeof(char *) * (nurls + wc + 1));
236 			if (!tmp) {
237 				_pam_log(LOG_ERR,
238 					 "DNS SRV %s", strerror(errno));
239 				goto dnssrv_free;
240 			}
241 
242 			urls = tmp;
243 			urls[nurls] = NULL;
244 
245 			for (i = 0; i < wc; i++) {
246 				char *p = malloc(strlen(lud->lud_scheme) +
247 						 strlen(wv[i]) +
248 						 3);
249 				if (!p) {
250 					_pam_log(LOG_ERR, "DNS SRV %s",
251 						 strerror(errno));
252 					goto dnssrv_free;
253 				}
254 
255 				strcpy(p, lud->lud_scheme);
256 				strcat(p, "//");
257 				strcat(p, wv[i]);
258 
259 				urls[nurls + i + 1] = NULL;
260 				urls[nurls + i] = p;
261 			}
262 
263 			nurls += i;
264 
265 		  dnssrv_free:
266 			argcv_free(wc, wv);
267 			ber_memfree(hostlist);
268 			ber_memfree(domain);
269 		} else {
270 			tmp = realloc(urls, sizeof(char *) * (nurls + 2));
271 			if (!tmp) {
272 				_pam_log(LOG_ERR,
273 					 "DNS SRV %s", strerror(errno));
274 				break;
275 			}
276 			urls = tmp;
277 			urls[nurls + 1] = NULL;
278 
279 			urls[nurls] = ldap_url_desc2str(lud);
280 			if (!urls[nurls]) {
281 				_pam_log(LOG_ERR, "DNS SRV %s",
282 					 strerror(errno));
283 				break;
284 			}
285 			nurls++;
286 		}
287 
288 		*ludp = lud->lud_next;
289 
290 		lud->lud_next = NULL;
291 		ldap_free_urldesc(lud);
292 	}
293 
294 	if (ludlist) {
295 		ldap_free_urldesc(ludlist);
296 		return NULL;
297 	} else if (!urls)
298 		return NULL;
299 	ldapuri = argcv_concat(nurls, urls);
300 	if (!ldapuri)
301 		_pam_log(LOG_ERR, "%s", strerror(errno));
302 	ber_memvfree((void **)urls);
303 	return ldapuri;
304 }
305 
306 static void ldap_unbind(LDAP *ld);
307 
308 static LDAP *
ldap_connect(struct gray_env * env)309 ldap_connect(struct gray_env *env)
310 {
311 	int rc;
312 	char *ldapuri = NULL;
313 	LDAP *ld = NULL;
314 	int protocol = LDAP_VERSION3;
315 	char *val;
316 	unsigned long lval;
317 	enum { tls_no, tls_yes,	tls_only } tls = tls_no;
318 
319 	if (ldap_debug_level) {
320 		if (ber_set_option(NULL, LBER_OPT_DEBUG_LEVEL,
321 				    &ldap_debug_level)
322 		    != LBER_OPT_SUCCESS )
323 			_pam_log(LOG_ERR,
324 				 "cannot set LBER_OPT_DEBUG_LEVEL %d",
325 				 ldap_debug_level);
326 
327 		if (ldap_set_option(NULL, LDAP_OPT_DEBUG_LEVEL,
328 				     &ldap_debug_level)
329 		    != LDAP_OPT_SUCCESS )
330 			_pam_log(LOG_ERR,
331 				 "could not set LDAP_OPT_DEBUG_LEVEL %d",
332 				 ldap_debug_level);
333 	}
334 
335 	val = gray_env_get(env, "uri");
336 	if (val) {
337 		ldapuri = parse_ldap_uri(val);
338 		if (!ldapuri)
339 			return NULL;
340 	}
341 	DEBUG(2, ("constructed LDAP URI: %s",
342 		  ldapuri ? ldapuri : "<DEFAULT>"));
343 
344 	rc = ldap_initialize(&ld, ldapuri);
345 	if (rc != LDAP_SUCCESS) {
346 		_pam_log(LOG_ERR,
347 			 "cannot create LDAP session handle for "
348 			 "URI=%s (%d): %s",
349 			 ldapuri, rc, ldap_err2string(rc));
350 		free(ldapuri);
351 		return NULL;
352 	}
353 	free(ldapuri);
354 
355 	if (get_intval(env, "ldap-version", 10, &lval) == 0) {
356 		switch (lval) {
357 		case 2:
358 			protocol = LDAP_VERSION2;
359 			break;
360 		case 3:
361 			protocol = LDAP_VERSION3;
362 			break;
363 		default:
364 			_pam_log(LOG_ERR,
365 				 "%s: invalid variable value, "
366 				 "defaulting to 3",
367 				 "ldap-version");
368 		}
369 	}
370 
371 	ldap_set_option(ld, LDAP_OPT_PROTOCOL_VERSION, &protocol);
372 
373 	val = gray_env_get(env, "tls");
374 
375 	if (val) {
376 		if (strcmp(val, "yes") == 0)
377 			tls = tls_yes;
378 		else if (strcmp(val, "no") == 0)
379 			tls = tls_no;
380 		else if (strcmp(val, "only") == 0)
381 			tls = tls_only;
382 		else {
383 			_pam_log(LOG_ERR,
384 				 "wrong value for tls statement, "
385 				 "assuming \"no\"");
386 			tls = tls_no;
387 		}
388 	} else {
389 		val = gray_env_get(env, "ssl");
390 		if (!val)
391 			tls = tls_no;
392 		else if (strcmp(val, "on") == 0)
393 			tls = tls_only;
394 		else if (strcmp(val, "start_tls") == 0)
395 			tls = tls_only;
396 		else
397 			tls = tls_no;
398 		/* FIXME:  "tls-reqcert" */
399 	}
400 
401 	if (tls != tls_no) {
402 		rc = ldap_start_tls_s(ld, NULL, NULL);
403 		if (rc != LDAP_SUCCESS) {
404 			char *msg = NULL;
405 			ldap_get_option(ld,
406 					LDAP_OPT_DIAGNOSTIC_MESSAGE,
407 					(void*)&msg);
408 			_pam_log(LOG_ERR,
409 				 "ldap_start_tls failed: %s",
410 				 ldap_err2string(rc));
411 			_pam_log(LOG_ERR,
412 				 "TLS diagnostics: %s", msg);
413 			ldap_memfree(msg);
414 
415 			if (tls == tls_only) {
416 				ldap_unbind(ld);
417 				return NULL;
418 			}
419 			/* try to continue anyway */
420 		} else {
421 			val = gray_env_get(env, "tls-cacert");
422 			if (val) {
423 				rc = ldap_set_option(ld,
424 						     LDAP_OPT_X_TLS_CACERTFILE,
425 						     val);
426 				if (rc != LDAP_SUCCESS) {
427 					_pam_log(LOG_ERR,
428 						 "setting of LDAP_OPT_X_TLS_CACERTFILE failed");
429 					if (tls == tls_only) {
430 						ldap_unbind(ld);
431 						return NULL;
432 					}
433 				}
434 			}
435 		}
436 	}
437 
438 	/* FIXME: Timeouts, SASL, etc. */
439 	return ld;
440 }
441 
442 static int
full_read(int fd,char * file,char * buf,size_t size)443 full_read(int fd, char *file, char *buf, size_t size)
444 {
445 	while (size) {
446 		ssize_t n;
447 
448 		n = read(fd, buf, size);
449 		if (n == -1) {
450 			if (errno == EAGAIN || errno == EINTR)
451 				continue;
452 			_pam_log(LOG_ERR, "error reading from %s: %s",
453 				 file, strerror(errno));
454 			return -1;
455 		} else if (n == 0) {
456 			_pam_log(LOG_ERR, "short read from %s", file);
457 			return -1;
458 		}
459 
460 		buf += n;
461 		size -= n;
462 	}
463 	return 0;
464 }
465 
466 static int
get_passwd(struct gray_env * env,struct berval * pwd,char ** palloc)467 get_passwd(struct gray_env *env, struct berval *pwd, char **palloc)
468 {
469 	char *file;
470 
471 	file = gray_env_get(env, "bindpwfile");
472 	if (file) {
473 		struct stat st;
474 		int fd, rc;
475 		char *mem, *p;
476 
477 		fd = open(file, O_RDONLY);
478 		if (fd == -1) {
479 			_pam_log(LOG_ERR, "can't open password file %s: %s",
480 				 file, strerror(errno));
481 			return -1;
482 		}
483 		if (fstat(fd, &st)) {
484 			_pam_log(LOG_ERR, "can't stat password file %s: %s",
485 				 file, strerror(errno));
486 			close(fd);
487 			return -1;
488 		}
489 		mem = malloc(st.st_size + 1);
490 		if (!mem) {
491 			_pam_log(LOG_ERR, "can't allocate memory (%lu bytes)",
492 				 (unsigned long) st.st_size+1);
493 			close(fd);
494 			return -1;
495 		}
496 		rc = full_read(fd, file, mem, st.st_size);
497 		close(fd);
498 		if (rc)
499 			return rc;
500 		mem[st.st_size] = 0;
501 		p = strchr(mem, '\n');
502 		if (p)
503 			*p = 0;
504 		*palloc = mem;
505 		pwd->bv_val = mem;
506 	} else
507 		pwd->bv_val = gray_env_get(env, "bindpw");
508 	pwd->bv_len = pwd->bv_val ? strlen(pwd->bv_val) : 0;
509 	return 0;
510 }
511 
512 static int
ldap_bind(LDAP * ld,struct gray_env * env)513 ldap_bind(LDAP *ld, struct gray_env *env)
514 {
515 	int msgid, err, rc;
516 	LDAPMessage *result;
517 	LDAPControl **ctrls;
518 	char msgbuf[256];
519 	char *matched = NULL;
520 	char *info = NULL;
521 	char **refs = NULL;
522 	struct berval passwd;
523 	char *binddn;
524 	char *alloc_ptr = NULL;
525 
526 	binddn = gray_env_get(env, "binddn");
527 
528 	if (get_passwd(env, &passwd, &alloc_ptr))
529 		return 1;
530 
531 	msgbuf[0] = 0;
532 
533 	rc = ldap_sasl_bind(ld, binddn, LDAP_SASL_SIMPLE, &passwd,
534 			    NULL, NULL, &msgid);
535 	if (msgid == -1) {
536 		_pam_log(LOG_ERR,
537 			 "ldap_sasl_bind(SIMPLE) failed: %s",
538 			 ldap_err2string(rc));
539 		free(alloc_ptr);
540 		return 1;
541 	}
542 
543 	if (ldap_result(ld, msgid, LDAP_MSG_ALL, NULL, &result ) == -1) {
544 		_pam_log(LOG_ERR, "ldap_result failed");
545 		free(alloc_ptr);
546 		return 1;
547 	}
548 
549 	rc = ldap_parse_result(ld, result, &err, &matched, &info, &refs,
550 			       &ctrls, 1);
551 	if (rc != LDAP_SUCCESS) {
552 		_pam_log(LOG_ERR, "ldap_parse_result failed: %s",
553 			 ldap_err2string(rc));
554 		free(alloc_ptr);
555 		return 1;
556 	}
557 
558 	if (ctrls)
559 		ldap_controls_free(ctrls);
560 
561 	if (err != LDAP_SUCCESS
562 	    || msgbuf[0]
563 	    || (matched && matched[0])
564 	    || (info && info[0])
565 	    || refs) {
566 
567 		DEBUG(2,("ldap_bind: %s (%d)%s",
568 			 ldap_err2string(err), err, msgbuf));
569 
570 		if (matched && *matched)
571 			DEBUG(2,("matched DN: %s", matched));
572 
573 		if (info && *info)
574 			DEBUG(2,("additional info: %s", info));
575 
576 		if (refs && *refs) {
577 			int i;
578 			DEBUG(3,("referrals:"));
579 			for (i = 0; refs[i]; i++)
580 				DEBUG(3,("%s", refs[i]));
581 		}
582 	}
583 
584 	if (matched)
585 		ber_memfree(matched);
586 	if (info)
587 		ber_memfree(info);
588 	if (refs)
589 		ber_memvfree((void **)refs);
590 
591 	free(alloc_ptr);
592 
593 	return !(err == LDAP_SUCCESS);
594 }
595 
596 static void
ldap_unbind(LDAP * ld)597 ldap_unbind(LDAP *ld)
598 {
599 	if (ld) {
600 		ldap_set_option(ld, LDAP_OPT_SERVER_CONTROLS, NULL);
601 		ldap_unbind_ext(ld, NULL, NULL);
602 	}
603 }
604 
605 static void
trimnl(char * s)606 trimnl(char *s)
607 {
608 	size_t len = strlen(s);
609 	while (len > 0 && s[len-1] == '\n')
610 		--len;
611 	s[len] = 0;
612 }
613 
614 static int
keycmp(const void * a,const void * b)615 keycmp(const void *a, const void *b)
616 {
617 	return strcmp(*(char**)a, *(char**)b);
618 }
619 
620 static char **
get_ldap_attrs(LDAP * ld,LDAPMessage * msg,const char * attr)621 get_ldap_attrs(LDAP *ld, LDAPMessage *msg, const char *attr)
622 {
623 	int rc, i, count;
624 	BerElement *ber = NULL;
625 	struct berval bv;
626 	char *ufn = NULL;
627 	char **ret;
628 	struct berval **values;
629 
630 	rc = ldap_get_dn_ber(ld, msg, &ber, &bv);
631 	if (rc != LDAP_SUCCESS) {
632 		_pam_log(LOG_ERR, "ldap_get_dn_ber: %s", ldap_err2string(rc));
633 		return NULL;
634 	}
635 	ufn = ldap_dn2ufn(bv.bv_val);
636 	DEBUG(2, ("INFO: %s", ufn));
637 	ldap_memfree(ufn);
638 
639 	values = ldap_get_values_len(ld, msg, attr);
640 	if (!values) {
641 		_pam_log(LOG_ERR,
642 			 "LDAP attribute `%s' has NULL value",
643 			 attr);
644 		return NULL;
645 	}
646 
647 	for (count = 0; values[count]; count++)
648 		;
649 
650 	ret = calloc(count + 1, sizeof(ret[0]));
651 	if (!ret)
652 		_pam_log(LOG_ERR, "%s", strerror(errno));
653 	else {
654 		for (i = 0; values[i]; i++) {
655 			char *p = malloc(values[i]->bv_len + 1);
656 			if (!p) {
657 				_pam_log(LOG_ERR, "%s", strerror(errno));
658 				break;
659 			}
660 			memcpy(p, values[i]->bv_val, values[i]->bv_len);
661 			p[values[i]->bv_len] = 0;
662 			trimnl(p);
663 			ret[i] = p;
664 			DEBUG(10,("pubkey: %s", p));
665 		}
666 
667 		if (i < count) {
668 			argcv_free(i, ret);
669 			ret = NULL;
670 		} else {
671 			ret[i] = NULL;
672 			qsort(ret, i, sizeof(ret[0]), keycmp);
673 		}
674 	}
675 
676 	ldap_value_free_len(values);
677 	return ret;
678 }
679 
680 static char **
get_pubkeys(LDAP * ld,const char * base,const char * filter,const char * attr)681 get_pubkeys(LDAP *ld, const char *base, const char *filter, const char *attr)
682 {
683 	int rc;
684 	LDAPMessage *res, *msg;
685 	ber_int_t msgid;
686 	char *attrs[2];
687 	char **ret;
688 
689 	attrs[0] = (char*) attr;
690 	attrs[1] = NULL;
691 	rc = ldap_search_ext(ld, base, LDAP_SCOPE_SUBTREE,
692 			     filter, attrs, 0,
693 			     NULL, NULL, NULL, -1, &msgid);
694 
695 	if (rc != LDAP_SUCCESS) {
696 		_pam_log(LOG_ERR, "ldap_search_ext: %s", ldap_err2string(rc));
697 		return NULL;
698 	}
699 
700 	rc = ldap_result(ld, msgid, LDAP_MSG_ALL, NULL, &res);
701 	if (rc < 0) {
702 		_pam_log(LOG_ERR, "ldap_result failed");
703 		return NULL;
704 	}
705 
706 	msg = ldap_first_entry(ld, res);
707 	if (!msg) {
708 		ldap_msgfree(res);
709 		return NULL;
710 	}
711 
712 	ret = get_ldap_attrs(ld, msg, attr);
713 
714 	ldap_msgfree(res);
715 
716 	return ret;
717 }
718 
719 static int
check_groups(int gc,char ** gv,const char * username,gid_t gid)720 check_groups(int gc, char **gv, const char *username, gid_t gid)
721 {
722 	int i;
723 	struct group *gp;
724 	char *pgname;
725 
726 	gp = getgrgid(gid);
727 	pgname = gp ? gray_strdup(gp->gr_name) : NULL;
728 	for (i = 0; i < gc; i++) {
729 		if (strcmp(gv[i], pgname) == 0) {
730 			free(pgname);
731 			return 0;
732 		}
733 		gp = getgrnam(gv[i]);
734 		if (gp) {
735 			char **p;
736 			for (p = gp->gr_mem; *p; p++)
737 				if (strcmp(username, *p) == 0) {
738 					free(pgname);
739 					return 0;
740 				}
741 		}
742 	}
743 	return 1;
744 }
745 
746 static int
check_user_groups(pam_handle_t * pamh,struct gray_env * env,struct passwd ** ppw,int * retval)747 check_user_groups(pam_handle_t *pamh, struct gray_env *env,
748 		  struct passwd **ppw, int *retval)
749 {
750 	int rc;
751 	const char *username;
752 	struct passwd *pw;
753 	unsigned long ival;
754 	char *sval;
755 
756 	rc = pam_get_user(pamh, &username, NULL);
757 	if (rc != PAM_SUCCESS || !username) {
758 		DEBUG(1,("can not get the username"));
759 		*retval = rc;
760 		return 1;
761 	}
762 	pw = getpwnam(username);
763 	if (!pw) {
764 		*retval = PAM_USER_UNKNOWN;
765 		return 1;
766 	}
767 	*ppw = pw;
768 	if (get_intval(env, "min-uid", 10, &ival) == 0) {
769 		if (pw->pw_uid < ival) {
770 			DEBUG(10, ("ignoring user %s: has UID < %lu",
771 				   username, ival));
772 			*retval = PAM_SUCCESS;
773 			return 1;
774 		}
775 	}
776 	if (get_intval(env, "min-gid", 10, &ival) == 0) {
777 		if (pw->pw_gid < ival) {
778 			DEBUG(10, ("ignoring user %s: has GID < %lu",
779 				   username, ival));
780 			*retval = PAM_SUCCESS;
781 			return 1;
782 		}
783 	}
784 	sval = gray_env_get(env, "allow-groups");
785 	if (sval) {
786 		int gc;
787 		char **gv;
788 		int rc;
789 
790 		if (argcv_split(sval, &gc, &gv)) {
791 			_pam_log(LOG_ERR, "cannot split allow-groups: %s",
792 				 strerror(errno));
793 			*retval = PAM_AUTH_ERR;
794 			return 1;
795 		}
796 		rc = check_groups(gc, gv, username, pw->pw_gid);
797 		argcv_free(gc, gv);
798 		if (rc) {
799 			DEBUG(10, ("ignoring user %s: not in allowed group list",
800 				   username, ival));
801 			*retval = PAM_SUCCESS;
802 			return 1;
803 		}
804 	}
805 	return 0;
806 }
807 
808 static int
copy(int src_fd,int dst_fd,char * buffer,size_t bufsize)809 copy(int src_fd, int dst_fd, char *buffer, size_t bufsize)
810 {
811 	ssize_t n;
812 
813 	while ((n = read(src_fd, buffer, bufsize)) > 0) {
814 		n = write(dst_fd, buffer, n);
815 		if (n < 0)
816 			break;
817 	}
818 	return n;
819 }
820 
821 static int
copy_file(pam_handle_t * pamh,const char * src,const char * dst,char * buffer,size_t bufsize,struct stat * st)822 copy_file(pam_handle_t *pamh, const char *src, const char *dst,
823 	  char *buffer, size_t bufsize, struct stat *st)
824 {
825 	int sfd, dfd, rc;
826 
827 	sfd = open(src, O_RDONLY);
828 	if (sfd == -1) {
829 		_pam_log(LOG_ERR, "cannot open %s: %s",
830 			 src, strerror(errno));
831 		return 1;
832 	}
833 
834 	dfd = open(dst, O_CREAT|O_TRUNC|O_RDWR, 0600);
835 	if (dfd == -1) {
836 		close(sfd);
837 		_pam_log(LOG_ERR, "cannot create %s: %s",
838 			 dst, strerror(errno));
839 		return 1;
840 	}
841 	if (fchown(dfd, st->st_uid, st->st_gid) ||
842 	    fchmod(dfd, st->st_mode & 07777)) {
843 		_pam_log(LOG_ERR, "cannot set privileges of %s: %s",
844 			 dst, strerror(errno));
845 		/* try to continue anyway */
846 	}
847 
848 	rc = copy(sfd, dfd, buffer, bufsize);
849 	if (rc)
850 		_pam_log(LOG_ERR, "I/O error copying %s to %s: %s",
851 			 src, dst, strerror(errno));
852 
853 	close(sfd);
854 	close(dfd);
855 
856 	return rc;
857 }
858 
859 #define INITIAL_READLINK_SIZE 128
860 
861 int
read_link_name(const char * name,char ** pbuf,size_t * psize,size_t * plen)862 read_link_name(const char *name, char **pbuf, size_t *psize, size_t *plen)
863 {
864 	int rc = 0;
865 	char *buf = *pbuf;
866 	size_t size = *psize;
867 	ssize_t linklen;
868 
869 	while (1) {
870 		if (!buf) {
871 			size = INITIAL_READLINK_SIZE;
872 			buf = malloc(size);
873 		} else {
874 			char *p;
875 			size_t newsize = size << 1;
876 			if (newsize < size) {
877 				rc = ENAMETOOLONG;
878 				break;
879 			}
880 			size = newsize;
881 			p = realloc(buf, size);
882 			if (!p)
883 				free(buf);
884 			buf = p;
885 		}
886 		if (!buf) {
887 			rc = 1;
888 			break;
889 		}
890 
891 		linklen = readlink(name, buf, size);
892 		if (linklen < 0 && errno != ERANGE) {
893 			rc = 1;
894 			break;
895 		}
896 
897 		if ((size_t) linklen < size) {
898 			buf[linklen++] = '\0';
899 			rc = 0;
900 			break;
901 		}
902 	}
903 
904 	if (rc) {
905 		if (buf) {
906 			free(buf);
907 			buf = NULL;
908 		}
909 		size = 0;
910 	}
911 	*pbuf = buf;
912 	*psize = size;
913 	if (plen)
914 		*plen = linklen;
915 	return rc;
916 }
917 
918 
919 static int
copy_link(pam_handle_t * pamh,const char * src,const char * dst,char * buffer,size_t bufsize,struct stat * st)920 copy_link(pam_handle_t *pamh, const char *src, const char *dst,
921 	  char *buffer, size_t bufsize, struct stat *st)
922 {
923 	char *lnkname = NULL;
924 	size_t lnklen = 0;
925 	int rc;
926 
927 	if (read_link_name(src, &lnkname, &lnklen, NULL)) {
928 		_pam_log(LOG_ERR, "error reading link %s: %s",
929 			 src, strerror(errno));
930 		return 1;
931 	}
932 	rc = symlink(lnkname, dst);
933 	if (rc)
934 		_pam_log(LOG_ERR, "can't link %s to %s: %s",
935 			 src, dst, strerror(errno));
936 	else if (lchown(dst, st->st_uid, st->st_gid)) {
937 		_pam_log(LOG_ERR, "cannot set privileges of %s: %s",
938 			 dst, strerror(errno));
939 		/* try to continue anyway */
940 	}
941 
942 	free(lnkname);
943 	return rc;
944 }
945 
946 /* Create the directory DIR, eventually creating all intermediate directories
947    starting from DIR + BASELEN. */
948 static int
create_hierarchy(char * dir,size_t baselen)949 create_hierarchy(char *dir, size_t baselen)
950 {
951 	int rc;
952 	struct stat st;
953 	char *p;
954 
955 	if (stat(dir, &st) == 0) {
956 		if (!S_ISDIR(st.st_mode)) {
957 			_pam_log(LOG_ERR, "component %s is not a directory",
958 				 dir);
959 			return 1;
960 		}
961 		return 0;
962 	} else if (errno != ENOENT) {
963 		_pam_log(LOG_ERR, "cannot stat file %s: %s",
964 		       dir, strerror(errno));
965 		return 1;
966 	}
967 
968 	p = strrchr(dir, '/');
969 	if (p) {
970 		if (p - dir + 1 < baselen) {
971 			_pam_log(LOG_ERR, "base directory %s does not exist",
972 				 dir);
973 			return 1;
974 		}
975 		*p = 0;
976 	}
977 
978 	rc = create_hierarchy(dir, baselen);
979 	if (rc == 0) {
980 		if (p)
981 			*p = '/';
982 		if (mkdir(dir, 0755)) {
983 			_pam_log(LOG_ERR, "cannot create directory %s: %s",
984 				 dir, strerror(errno));
985 			rc = 1;
986 		}
987 	}
988 	return rc;
989 }
990 
991 static int
create_interdir(const char * path,struct passwd * pw)992 create_interdir(const char *path, struct passwd *pw)
993 {
994 	char *dir, *p;
995 	size_t len;
996 	int rc;
997 
998 	p = strrchr(path, '/');
999 	if (!p)
1000 		return 1;
1001 	len = p - path;
1002 	dir = gray_malloc(len + 1);
1003 	memcpy(dir, path, len);
1004 	dir[len] = 0;
1005 	rc = create_hierarchy(dir, strlen(pw->pw_dir));
1006 	if (rc == 0)
1007 		rc = chown(dir, pw->pw_uid, pw->pw_gid);
1008 	free(dir);
1009 	return rc;
1010 }
1011 
1012 struct namebuf {
1013 	char *name;
1014 	size_t size;
1015 	size_t prefix_len;
1016 };
1017 
1018 static void
namebuf_trimslash(struct namebuf * buf)1019 namebuf_trimslash(struct namebuf *buf)
1020 {
1021 	size_t len = strlen(buf->name);
1022 	while (len > 0 && buf->name[len-1] == '/')
1023 		--len;
1024 	buf->name[len] = 0;
1025 }
1026 
1027 static int
namebuf_init(struct namebuf * buf,const char * name)1028 namebuf_init(struct namebuf *buf, const char *name)
1029 {
1030 	buf->name = strdup(name);
1031 	if (!buf->name)
1032 		return 1;
1033 	buf->prefix_len = strlen(name);
1034 	buf->size = buf->prefix_len + 1;
1035 	namebuf_trimslash(buf);
1036 	return 0;
1037 }
1038 
1039 static int
namebuf_set(struct namebuf * buf,const char * name)1040 namebuf_set(struct namebuf *buf, const char *name)
1041 {
1042 	size_t len;
1043 
1044 	if (!buf->name)
1045 		return namebuf_init(buf, name);
1046 	len = strlen(name);
1047 	if (buf->prefix_len + len + 1 > buf->size) {
1048 		size_t ns;
1049 		char *np;
1050 
1051 		for (ns = buf->size; buf->prefix_len + len + 1 > ns;
1052 		     ns += ns)
1053 			;
1054 
1055 		np = realloc(buf->name, ns);
1056 		if (!np)
1057 			return 1;
1058 		buf->size = ns;
1059 		buf->name = np;
1060 	}
1061 
1062 	strcpy(buf->name + buf->prefix_len, name);
1063 	//namebuf_trimslash(buf);
1064 
1065 	return 0;
1066 }
1067 
1068 static size_t
namebuf_set_prefix(struct namebuf * buf)1069 namebuf_set_prefix(struct namebuf *buf)
1070 {
1071 	size_t ret;
1072 	ret = buf->prefix_len;
1073 	buf->prefix_len = strlen(buf->name);
1074 	if (namebuf_set(buf, "/"))
1075 		return 1;
1076 	++buf->prefix_len;
1077 	return ret;
1078 }
1079 
1080 static void
namebuf_set_prefix_len(struct namebuf * buf,size_t len)1081 namebuf_set_prefix_len(struct namebuf *buf, size_t len)
1082 {
1083 	buf->prefix_len = len;
1084 	buf->name[len] = 0;
1085 }
1086 
1087 static int recursive_copy(pam_handle_t *pamh, DIR *dir,
1088 			  struct namebuf *srcbuf, struct namebuf *dstbuf,
1089 			  char *buffer, size_t bufsize, struct passwd *pw,
1090 			  struct stat *st);
1091 
1092 static int
dir_copy_loop(pam_handle_t * pamh,DIR * dir,struct namebuf * srcbuf,struct namebuf * dstbuf,char * buffer,size_t bufsize,struct passwd * pw)1093 dir_copy_loop(pam_handle_t *pamh, DIR *dir,
1094 	      struct namebuf *srcbuf, struct namebuf *dstbuf,
1095 	      char *buffer, size_t bufsize, struct passwd *pw)
1096 {
1097 	struct dirent *ent;
1098 
1099 	while ((ent = readdir(dir))) {
1100 		char const *ename = ent->d_name;
1101 		struct stat st;
1102 		int rc;
1103 
1104 		if (ename[ename[0] != '.' ? 0 : ename[1] != '.' ? 1 : 2] == 0)
1105 			continue;
1106 		if (namebuf_set(srcbuf, ename)) {
1107 			_pam_log(LOG_ERR, "copy error: %s", strerror(errno));
1108 			return 1;
1109 		}
1110 		if (namebuf_set(dstbuf, ename)) {
1111 			_pam_log(LOG_ERR, "copy error: %s", strerror(errno));
1112 			return 1;
1113 		}
1114 		if (lstat(srcbuf->name, &st)) {
1115 			_pam_log(LOG_ERR, "cannot stat %s: %s",
1116 				 srcbuf->name, strerror(errno));
1117 			return 1;
1118 		}
1119 		st.st_uid = pw->pw_uid;
1120 		st.st_gid = pw->pw_gid;
1121 		if (S_ISREG(st.st_mode))
1122 			rc = copy_file(pamh, srcbuf->name, dstbuf->name,
1123 				       buffer, bufsize, &st);
1124 		else if (S_ISDIR(st.st_mode)) {
1125 			DIR *nd = opendir(srcbuf->name);
1126 			if (!nd) {
1127 				_pam_log(LOG_ERR,
1128 					 "cannot open directory %s: %s",
1129 					 srcbuf->name, strerror(errno));
1130 				rc = 1;
1131 			} else {
1132 				size_t srclen = namebuf_set_prefix(srcbuf);
1133 				size_t dstlen = namebuf_set_prefix(dstbuf);
1134 				rc = recursive_copy(pamh, nd, srcbuf, dstbuf,
1135 						    buffer, bufsize, pw,
1136 						    &st);
1137 				closedir(nd);
1138 				namebuf_set_prefix_len(dstbuf, dstlen);
1139 				namebuf_set_prefix_len(srcbuf, srclen);
1140 			}
1141 		} else if (S_ISLNK(st.st_mode))
1142 			rc = copy_link(pamh, srcbuf->name, dstbuf->name,
1143 				       buffer, bufsize, &st);
1144 		else {
1145 			_pam_log(LOG_NOTICE,
1146 				 "ignoring file %s: unsupported file type",
1147 				 srcbuf->name);
1148 			rc = 0;
1149 		}
1150 
1151 		if (rc)
1152 			return 1;
1153 	}
1154 	return 0;
1155 }
1156 
1157 static int
recursive_copy(pam_handle_t * pamh,DIR * dir,struct namebuf * srcbuf,struct namebuf * dstbuf,char * buffer,size_t bufsize,struct passwd * pw,struct stat * st)1158 recursive_copy(pam_handle_t *pamh, DIR *dir,
1159 	       struct namebuf *srcbuf, struct namebuf *dstbuf,
1160 	       char *buffer, size_t bufsize, struct passwd *pw,
1161 	       struct stat *st)
1162 {
1163 	int rc;
1164 	struct stat dst_st;
1165 
1166 	if (stat(dstbuf->name, &dst_st)) {
1167 		if (errno == ENOENT) {
1168 			if (mkdir(dstbuf->name, 0700)) {
1169 				_pam_log(LOG_ERR, "cannot create %s: %s",
1170 					 dstbuf->name, strerror(errno));
1171 				return 1;
1172 			}
1173 		} else {
1174 			_pam_log(LOG_ERR, "cannot stat %s: %s",
1175 				 dstbuf->name, strerror(errno));
1176 			return 1;
1177 		}
1178 	}
1179 
1180 	rc = dir_copy_loop(pamh, dir, srcbuf, dstbuf, buffer, bufsize, pw);
1181 	dstbuf->name[dstbuf->prefix_len-1] = 0;
1182 	if (chown(dstbuf->name, pw->pw_uid, pw->pw_gid) ||
1183 	    (st && chmod(dstbuf->name, st->st_mode & 07777))) {
1184 		_pam_log(LOG_ERR,
1185 			 "cannot set privileges for %s:"
1186 			 "%s",
1187 			 dstbuf->name,
1188 			 strerror(errno));
1189 	}
1190 
1191 	return rc;
1192 }
1193 
1194 #define MIN_BUF_SIZE 2
1195 #define MAX_BUF_SIZE 16384
1196 
1197 static int
populate_homedir(pam_handle_t * pamh,struct passwd * pw,struct gray_env * env)1198 populate_homedir(pam_handle_t *pamh, struct passwd *pw, struct gray_env *env)
1199 {
1200 	const char *skel;
1201 	char *buffer;
1202 	size_t bufsize;
1203 	struct stat st;
1204 	unsigned long n;
1205 	DIR *dir;
1206 	int rc;
1207 
1208 	skel = gray_env_get(env, "skel");
1209 	if (!skel)
1210 		return 0;
1211 
1212 	if (stat(skel, &st)) {
1213 		_pam_log(LOG_ERR, "cannot stat skeleton directory %s: %s",
1214 			 pw->pw_dir, strerror(errno));
1215 		return 1;
1216 	} else if (!S_ISDIR(st.st_mode)) {
1217 		_pam_log(LOG_ERR, "%s exists, but is not a directory",
1218 			 pw->pw_dir);
1219 		return 1;
1220 	}
1221 
1222 	if (get_intval(env, "copy-buf-size", 10, &n) == 0)
1223 		bufsize = n;
1224 	else
1225 		bufsize = MAX_BUF_SIZE;
1226 
1227 	for (; (buffer = malloc(bufsize)) == NULL; bufsize >>= 1)
1228 		if (bufsize < MIN_BUF_SIZE)
1229 			return ENOMEM;
1230 
1231 	dir = opendir(skel);
1232 	if (!dir) {
1233 		_pam_log(LOG_ERR, "cannot open skeleton directory %s: %s",
1234 			 skel, strerror(errno));
1235 		rc = 1;
1236 	} else {
1237 		struct namebuf srcbuf, dstbuf;
1238 
1239 		if (namebuf_init(&srcbuf, skel) == 0) {
1240 			namebuf_set_prefix(&srcbuf);
1241 			if (namebuf_init(&dstbuf, pw->pw_dir) == 0) {
1242 				namebuf_set_prefix(&dstbuf);
1243 				rc = recursive_copy(pamh, dir,
1244 						    &srcbuf, &dstbuf,
1245 						    buffer, bufsize, pw,
1246 						    NULL);
1247 				free(dstbuf.name);
1248 			} else
1249 				rc = 1;
1250 			free(srcbuf.name);
1251 		} else
1252 			rc = 1;
1253 		closedir(dir);
1254 	}
1255 	free(buffer);
1256 	return rc;
1257 }
1258 
1259 /* Operations on public key files */
1260 
1261 struct pubkeyfile {
1262 	char *file_name;   /* Name of the file */
1263 	int fd;            /* File descriptor */
1264 	char *base;        /* File contents */
1265 	size_t size;       /* Size of base */
1266 	char **lnv;        /* File contents parsed into nul-terminated lines */
1267 	size_t lnc;        /* Number of lines in lnv */
1268 	size_t lnm;        /* Max. capacity of lnv */
1269 };
1270 
1271 /* Open public key file NAME.  Return 0 on success.  On error, issue a
1272    diagnostic message and return -1. */
1273 static int
pubkeyfile_open(struct pubkeyfile * pkb,char * name)1274 pubkeyfile_open(struct pubkeyfile *pkb, char *name)
1275 {
1276 	memset(pkb, 0, sizeof *pkb);
1277 	pkb->fd = open(name, O_CREAT|O_RDWR, 0666);
1278 	if (pkb->fd == -1) {
1279 		_pam_log(LOG_ERR, "can't open %s: %s",
1280 			 name, strerror(errno));
1281 		return -1;
1282 	}
1283 	pkb->file_name = gray_strdup(name);
1284 	return 0;
1285 }
1286 
1287 /* Read in the contents of the open public key file PKB. */
1288 static int
pubkeyfile_read(struct pubkeyfile * pkb)1289 pubkeyfile_read(struct pubkeyfile *pkb)
1290 {
1291 	struct stat st;
1292 	char *p;
1293 	size_t i;
1294 
1295 	if (fstat(pkb->fd, &st)) {
1296 		_pam_log(LOG_ERR, "fstat %s: %s",
1297 			 pkb->file_name, strerror(errno));
1298 		return -1;
1299 	}
1300 	pkb->size = st.st_size;
1301 	pkb->base = gray_malloc(st.st_size + 1);
1302 	if (full_read(pkb->fd, pkb->file_name, pkb->base, pkb->size)) {
1303 		_pam_log(LOG_ERR, "fread %s: %s",
1304 			 pkb->file_name, strerror(errno));
1305 		return -1;
1306 	}
1307 	pkb->base[pkb->size] = 0;
1308 	pkb->lnc = 0;
1309 	for (p = pkb->base; *p; p++)
1310 		if (*p == '\n')
1311 			++pkb->lnc;
1312 	pkb->lnm = pkb->lnc + 1;
1313 	pkb->lnv = gray_calloc(pkb->lnm, sizeof(pkb->lnv[0]));
1314 
1315 	i = 0;
1316 	for (p = pkb->base; *p; p++) {
1317 		if (p == pkb->base || p[-1] == 0)
1318 			pkb->lnv[i++] = p;
1319 		if (*p == '\n')
1320 			*p = 0;
1321 	}
1322 	pkb->lnv[i] = NULL;
1323 	return 0;
1324 }
1325 
1326 /* Open the public key file NAME and read its contents. */
1327 static int
pubkeyfile_init(struct pubkeyfile * pkb,char * name)1328 pubkeyfile_init(struct pubkeyfile *pkb, char *name)
1329 {
1330 	if (pubkeyfile_open(pkb, name))
1331 		return -1;
1332 	return pubkeyfile_read(pkb);
1333 }
1334 
1335 /* Write data from lnv into the public key file, overwriting its current
1336    content. */
1337 static int
pubkeyfile_write(struct pubkeyfile * pkb)1338 pubkeyfile_write(struct pubkeyfile *pkb)
1339 {
1340 	int i;
1341 
1342 	if (lseek(pkb->fd, 0, SEEK_SET)) {
1343 		_pam_log(LOG_ERR, "lseek %s: %s",
1344 			 pkb->file_name, strerror(errno));
1345 		return -1;
1346 	}
1347 	if (ftruncate(pkb->fd, 0)) {
1348 		_pam_log(LOG_ERR, "ftruncate %s: %s",
1349 			 pkb->file_name, strerror(errno));
1350 		return -1;
1351 	}
1352 
1353 	for (i = 0; i < pkb->lnc; i++) {
1354 		if (pkb->lnv[i]) {
1355 			static char newline = '\n';
1356 			size_t len = strlen(pkb->lnv[i]);
1357 			if (write(pkb->fd, pkb->lnv[i], len) != len
1358 			    || write(pkb->fd, &newline, 1) != 1) {
1359 				_pam_log(LOG_ERR, "error writing %s: %s",
1360 					 pkb->file_name, strerror(errno));
1361 				return -1;
1362 			}
1363 		}
1364 	}
1365 	return 0;
1366 }
1367 
1368 /* Remove COUNT lines starting from position POS in PKB. */
1369 static void
pubkeyfile_remove_lines(struct pubkeyfile * pkb,int pos,int count)1370 pubkeyfile_remove_lines(struct pubkeyfile *pkb, int pos, int count)
1371 {
1372 	if (count == 0)
1373 		return;
1374 	if (pos > pkb->lnc) {
1375 		_pam_log(LOG_ERR, "%s:%d: INTERNAL ERROR: pos out of range",
1376 			 __FILE__, __LINE__);
1377 		abort();
1378 	}
1379 	if (pos + count > pkb->lnc) {
1380 		_pam_log(LOG_ERR, "%s:%d: INTERNAL ERROR: count out of range",
1381 			 __FILE__, __LINE__);
1382 		abort();
1383 	}
1384 	memmove(pkb->lnv + pos, pkb->lnv + pos + count,
1385 		(pkb->lnc - pos - count + 1) * sizeof(pkb->lnv[0]));
1386 	pkb->lnc -= count;
1387 }
1388 
1389 /* Allocate COUNT lines starting from position POS in PKB, preserving
1390    the existing data. */
1391 static void
pubkeyfile_alloc_lines(struct pubkeyfile * pkb,size_t pos,size_t count)1392 pubkeyfile_alloc_lines(struct pubkeyfile *pkb, size_t pos, size_t count)
1393 {
1394 	if (pos > pkb->lnc) {
1395 		_pam_log(LOG_ERR, "%s:%d: INTERNAL ERROR: pos out of range",
1396 			 __FILE__, __LINE__);
1397 		abort();
1398 	}
1399 	if (pkb->lnc + count + 1 > pkb->lnm) {
1400 		pkb->lnm += count;
1401 		pkb->lnv = gray_realloc(pkb->lnv,
1402 					pkb->lnm * sizeof(pkb->lnv[0]));
1403 	}
1404 	memmove(pkb->lnv + pos + count, pkb->lnv + pos,
1405 		(pkb->lnc - pos + 1) * sizeof(pkb->lnv[0]));
1406 	pkb->lnc += count;
1407 }
1408 
1409 /* Insert lines from LV in position POS in the file PKB, shifting down
1410    existing lines as necessary. */
1411 void
pubkeyfile_insert_lines(struct pubkeyfile * pkb,size_t pos,char ** lv)1412 pubkeyfile_insert_lines(struct pubkeyfile *pkb, size_t pos, char **lv)
1413 {
1414 	size_t i;
1415 	size_t lc;
1416 
1417 	for (lc = 0; lv[lc]; lc++)
1418 		;
1419 
1420         pubkeyfile_alloc_lines(pkb, pos, lc);
1421 
1422 	for (i = 0; i < lc; i++)
1423 		pkb->lnv[pos + i] = lv[i];
1424 }
1425 
1426 /* Close the public key file */
1427 void
pubkeyfile_close(struct pubkeyfile * pkb)1428 pubkeyfile_close(struct pubkeyfile *pkb)
1429 {
1430 	close(pkb->fd);
1431 	free(pkb->file_name);
1432 	free(pkb->base);
1433 	free(pkb->lnv);
1434 }
1435 
1436 
1437 static int
store_pubkeys(char ** keys,struct passwd * pw,struct gray_env * env)1438 store_pubkeys(char **keys, struct passwd *pw, struct gray_env *env)
1439 {
1440 	int rc;
1441 	char *file_name;
1442 	size_t homelen, pathlen, len;
1443 	int retval, i, j;
1444 	int update = 0;
1445 	int oldmask;
1446 	unsigned long mode;
1447 	struct pubkeyfile pkf;
1448 
1449 	homelen = strlen(pw->pw_dir);
1450 	pathlen = strlen(authorized_keys_file);
1451 	len = homelen + pathlen;
1452 	if (pw->pw_dir[homelen - 1] != '/')
1453 		len++;
1454 	file_name = gray_malloc(len + 1);
1455 	memcpy(file_name, pw->pw_dir, homelen);
1456 	if (pw->pw_dir[homelen - 1] != '/')
1457 		file_name[homelen++] = '/';
1458 	strcpy(file_name + homelen, authorized_keys_file);
1459 
1460 	switch (get_intval(env, "keyfile-mode", 8, &mode)) {
1461 	case -1:
1462 		return PAM_SERVICE_ERR;
1463 	case 1:
1464 		oldmask = -1;
1465 		break;
1466 	case 0:
1467 		oldmask = umask(0666 ^ (mode & 0777));
1468 	}
1469 
1470 	if (access(file_name, R_OK)
1471 	    && create_interdir(file_name, pw) == 0) {
1472 		update = 1;
1473 		i = 0;
1474 	}
1475 
1476 	rc = pubkeyfile_init(&pkf, file_name);
1477 
1478 	if (oldmask != -1)
1479 		umask(oldmask);
1480 
1481 	if (rc) {
1482 		free(file_name);
1483 		return PAM_SERVICE_ERR;
1484 	}
1485 	if (fchown(pkf.fd, pw->pw_uid, pw->pw_gid))
1486 		_pam_log(LOG_ERR, "chown %s: %s",
1487 			 file_name, strerror(errno));
1488 
1489 	if (!update) {
1490 		char *boundary = gray_env_get(env, "user-keys-boundary");
1491 
1492 		j = 0;
1493 		for (i = 0; i < pkf.lnc; i++) {
1494 			char *kp;
1495 			char *p = pkf.lnv[i];
1496 			if (*p == '#') {
1497 				if (boundary && strcmp(p + 1, boundary) == 0)
1498 					break;
1499 				continue;
1500 			}
1501 			if (update)
1502 				continue;
1503 			if (*p == 0)
1504 				continue;
1505 			kp = keys[j++];
1506 
1507 			if (!kp) {
1508 				DEBUG(2, ("less keys in the database"));
1509 				update = 1;
1510 			} else if (strcmp(p, kp)) {
1511 				DEBUG(2, ("key %d mismatch", j));
1512 				update = 1;
1513 			}
1514 		}
1515 		if (!update && keys[j]) {
1516 			DEBUG(2, ("more keys in the database"));
1517 			update = 1;
1518 		}
1519 	}
1520 
1521 	if (update) {
1522 		pubkeyfile_remove_lines(&pkf, 0, i);
1523 		pubkeyfile_insert_lines(&pkf, 0, keys);
1524 		pubkeyfile_write(&pkf);
1525 		retval = PAM_TRY_AGAIN;
1526 	} else
1527 		retval = PAM_SUCCESS;
1528 	pubkeyfile_close(&pkf);
1529 	free(file_name);
1530 
1531 	return retval;
1532 }
1533 
1534 static int
import_public_key(pam_handle_t * pamh,struct passwd * pw,struct gray_env * env)1535 import_public_key(pam_handle_t *pamh, struct passwd *pw, struct gray_env *env)
1536 {
1537 	LDAP *ld;
1538 	int retval;
1539 	const char *base = gray_env_get(env, "base");
1540 	const char *filter_pat = gray_env_get(env, "filter");
1541 	const char *attr = gray_env_get(env, "pubkey-attr");
1542 
1543 	if (!gray_env_get_bool(env, "import-public-keys", 1))
1544 		return PAM_SUCCESS;
1545 
1546 	if (!filter_pat) {
1547 		_pam_log(LOG_ERR, "configuration variable `filter' not set");
1548 		return PAM_SERVICE_ERR;
1549 	}
1550 	if (!attr) {
1551 		_pam_log(LOG_ERR, "configuration variable `attr' not set");
1552 		return PAM_SERVICE_ERR;
1553 	}
1554 
1555 	ld = ldap_connect(env);
1556 	if (!ld)
1557 		return PAM_SERVICE_ERR;
1558 	if (ldap_bind(ld, env))
1559 		retval = PAM_SERVICE_ERR;
1560 	else {
1561 		char *filter;
1562 		gray_slist_t slist;
1563 		char **keys;
1564 
1565 		slist = gray_slist_create();
1566 		gray_expand_string(pamh, filter_pat, slist);
1567 		gray_slist_append_char(slist, 0);
1568 		filter = gray_slist_finish(slist);
1569 
1570 		keys = get_pubkeys(ld, base, filter, attr);
1571 		gray_slist_free(&slist);
1572 		if (keys) {
1573 			retval = store_pubkeys(keys, pw, env);
1574 			argcvz_free(keys);
1575 		} else
1576 			retval = PAM_SUCCESS;
1577 	}
1578 	ldap_unbind(ld);
1579 	return retval;
1580 }
1581 
1582 static int
dir_in_path(const char * dir,const char * path)1583 dir_in_path(const char *dir, const char *path)
1584 {
1585 	char *p;
1586 	size_t dirlen;
1587 
1588 	p = strrchr(dir, '/');
1589 	if (p)
1590 		dirlen = p - dir;
1591 	else
1592 		return 0;
1593 
1594 	while (*path) {
1595 		size_t len = strcspn(path, ":");
1596 		while (len > 0 && path[len-1] == '/')
1597 			--len;
1598 		if (len == dirlen && memcmp(path, dir, len) == 0)
1599 			return 1;
1600 		path += len;
1601 		if (*path == ':')
1602 			++path;
1603 	}
1604 	return 0;
1605 }
1606 
1607 enum create_status {
1608 	create_ok,
1609 	create_exists,
1610 	create_failure,
1611 	create_skip
1612 };
1613 
1614 static enum create_status
create_home_dir(pam_handle_t * pamh,struct passwd * pw,struct gray_env * env)1615 create_home_dir(pam_handle_t *pamh, struct passwd *pw, struct gray_env *env)
1616 {
1617 	struct stat st;
1618 
1619 	if (stat(pw->pw_dir, &st)) {
1620 		unsigned long mode = 0755;
1621 		char *val;
1622 
1623 		if (errno != ENOENT) {
1624 			_pam_log(LOG_ERR, "cannot stat home directory %s: %s",
1625 				 pw->pw_dir, strerror(errno));
1626 			return create_failure;
1627 		}
1628 
1629 		val = gray_env_get(env, "allow-home-dir");
1630 		if (val && !dir_in_path(pw->pw_dir, val))
1631 			return create_skip;
1632 
1633 		if (get_intval(env, "home-dir-mode", 8, &mode) == -1)
1634 			return create_failure;
1635 		mode &= 07777;
1636 		if (mkdir(pw->pw_dir, 0700)) {
1637 			_pam_log(LOG_ERR, "cannot create %s: %s",
1638 				 pw->pw_dir, strerror(errno));
1639 			return create_failure;
1640 		}
1641 		populate_homedir(pamh, pw, env);
1642 		if (chown(pw->pw_dir, pw->pw_uid, pw->pw_gid) ||
1643 		    chmod(pw->pw_dir, mode)) {
1644 			_pam_log(LOG_ERR,
1645 				 "cannot change mode or ownership of %s: %s",
1646 				 pw->pw_dir, strerror(errno));
1647 			return create_failure;
1648 		}
1649 	} else if (!S_ISDIR(st.st_mode)) {
1650 		_pam_log(LOG_ERR, "%s exists, but is not a directory",
1651 			 pw->pw_dir);
1652 		return create_failure;
1653 	} else
1654 		return create_exists;
1655 
1656 	return create_ok;
1657 }
1658 
1659 extern char **environ;
1660 
1661 static char *
find_env(char * name,int val)1662 find_env(char *name, int val)
1663 {
1664         int nlen = strcspn(name, "?+=");
1665         int i;
1666 
1667         for (i = 0; environ[i]; i++) {
1668                 size_t elen = strcspn(environ[i], "=");
1669                 if (elen == nlen && memcmp(name, environ[i], nlen) == 0)
1670                         return val ? environ[i] + elen + 1 : environ[i];
1671         }
1672         return NULL;
1673 }
1674 
1675 static int
locate_unset(char ** env,const char * name)1676 locate_unset(char **env, const char *name)
1677 {
1678         volatile int i;
1679         int nlen = strcspn(name, "=");
1680 
1681         for (i = 0; env[i]; i++) {
1682                 if (env[i][0] == '-') {
1683                         size_t elen = strcspn(env[i] + 1, "=");
1684                         if (elen == nlen
1685                             && memcmp(name, env[i] + 1, nlen) == 0) {
1686                                 if (env[i][nlen + 1])
1687                                         return strcmp(name + nlen,
1688                                                       env[i] + 1 + nlen) == 0;
1689                                 else
1690                                         return 1;
1691                         }
1692                 }
1693         }
1694         return 0;
1695 }
1696 
1697 static char *
env_concat(char * name,size_t namelen,char * a,char * b)1698 env_concat(char *name, size_t namelen, char *a, char *b)
1699 {
1700         char *res;
1701         size_t len;
1702 
1703         if (a && b) {
1704                 res = gray_malloc(namelen + 1 + strlen(a) + strlen(b) + 1);
1705                 strcpy(res + namelen + 1, a);
1706                 strcat(res, b);
1707         } else if (a) {
1708                 len = strlen(a);
1709                 if (ispunct(a[len-1]))
1710                         len--;
1711                 res = gray_malloc(namelen + 1 + len + 1);
1712                 memcpy(res + namelen + 1, a, len);
1713                 res[namelen + 1 + len] = 0;
1714         } else /* if (a == NULL) */ {
1715                 if (ispunct(b[0]))
1716                         b++;
1717 		len = strlen(b);
1718                 res = gray_malloc(namelen + 1 + len + 1);
1719                 strcpy(res + namelen + 1, b);
1720         }
1721         memcpy(res, name, namelen);
1722         res[namelen] = '=';
1723         return res;
1724 }
1725 
1726 static char **
parsenv(char * str)1727 parsenv(char *str)
1728 {
1729 	enum {
1730 		st_init,
1731 		st_kwd,
1732 		st_val,
1733 		st_eq,
1734 		st_dquote,
1735 		st_squote,
1736 		st_end
1737 	} state = st_init, prev_state;
1738 # define setstate(s) do { prev_state = state; state = s; } while (0)
1739 	char *p, *kw;
1740 	char **wv = NULL;
1741 	size_t wi = 0, wc = 0;
1742 
1743 	if (!str)
1744 		return NULL;
1745 
1746 	for (p = str; *p; ++p) {
1747 		switch (state) {
1748 		case st_init:
1749 			if (*p == ' ' || *p == '\t')
1750 				continue;
1751 			setstate(st_kwd);
1752 			kw = p;
1753 			break;
1754 		case st_kwd:
1755 			if (*p == ' ' || *p == '\t') {
1756 				setstate(st_end);
1757 			} else if (*p == '=') {
1758 				setstate(st_eq);
1759 			}
1760 			break;
1761 		case st_eq:
1762 			if (*p == '"') {
1763 				setstate(st_dquote);
1764 			} else if (*p == '\'') {
1765 				setstate(st_squote);
1766 			} else {
1767 				setstate(st_val);
1768 			}
1769 			/* fall through */
1770 		case st_val:
1771 			if (*p == ' ' || *p == '\t')
1772 				setstate(st_end);
1773 			break;
1774 		case st_dquote:
1775 			if (*p == '\\')
1776 				++p;
1777 			else if (*p == '"')
1778 				setstate(st_end);
1779 			break;
1780 		case st_squote:
1781 			if (*p == '\'')
1782 				setstate(st_end);
1783 			break;
1784 		case st_end:
1785 			/* can't happen */
1786 			break;
1787 		}
1788 
1789 		if (state == st_end) {
1790 			size_t len = p - kw;
1791 			char *q;
1792 
1793 			if (wi == wc) {
1794 				if (wc == 0)
1795 					wc = 4;
1796 				else
1797 					wc *= 2;
1798 				wv = gray_realloc(wv, wc * sizeof(wv[0]));
1799 			}
1800 
1801 			switch (prev_state) {
1802 			case st_squote:
1803 				len -= 2;
1804 				wv[wi] = gray_malloc(len + 1);
1805 				for (q = wv[wi]; *kw; ) {
1806 					if (*kw == '\'')
1807 						++kw;
1808 					else
1809 						*q++ = *kw++;
1810 				}
1811 				*q = 0;
1812 				break;
1813 			case st_dquote:
1814 				len -= 2;
1815 				wv[wi] = gray_malloc(len + 1);
1816 				q = wv[wi];
1817 				while ((*q++ = *kw++) != '=')
1818 					;
1819 				while (*kw != '"')
1820 					*q++ = *kw++;
1821 				++kw;
1822 				while (*kw != '"') {
1823 					if (*kw == '\\')
1824 						++kw;
1825 					*q++ = *kw++;
1826 				}
1827 				*q = 0;
1828 				break;
1829 			default:
1830 				wv[wi] = gray_malloc(len + 1);
1831 				memcpy(wv[wi], kw, len);
1832 				wv[wi][len] = 0;
1833 			}
1834 			++wi;
1835 			setstate(st_init);
1836 		}
1837 	}
1838 
1839 	if (state != st_init) {
1840 		if (wc == wi) {
1841 			++wc;
1842 			wv = gray_realloc(wv, (wc + 1) * sizeof(wv[0]));
1843 		}
1844 		wv[wi++] = gray_strdup(kw);
1845 	}
1846 
1847 	if (wc == wi)
1848 		wv = gray_realloc(wv, (wc + 1) * sizeof(wv[0]));
1849 	wv[wi] = NULL;
1850 
1851 	return wv;
1852 }
1853 
1854 static char **
env_setup(char * envstr)1855 env_setup(char *envstr)
1856 {
1857 	char **env;
1858         char **old_env = environ;
1859         char **new_env;
1860         int count, i, n;
1861 
1862 	env = parsenv(envstr);
1863 
1864         if (!env)
1865                 return old_env;
1866 
1867         if (strcmp(env[0], "-") == 0) {
1868                 old_env = NULL;
1869                 env++;
1870         }
1871 
1872         /* Count new environment size */
1873         count = 0;
1874         if (old_env)
1875                 for (i = 0; old_env[i]; i++)
1876                         count++;
1877 
1878         for (i = 0; env[i]; i++)
1879                 count++;
1880 
1881         /* Allocate the new environment. */
1882         new_env = gray_calloc(count + 1, sizeof new_env[0]);
1883 
1884         /* Populate the environment. */
1885         n = 0;
1886 
1887         if (old_env)
1888                 for (i = 0; old_env[i]; i++) {
1889                         if (!locate_unset(env, old_env[i]))
1890                                 new_env[n++] = old_env[i];
1891                 }
1892 
1893         for (i = 0; env[i]; i++) {
1894                 char *p;
1895 
1896                 if (env[i][0] == '-') {
1897                         /* Skip unset directives. */
1898                         continue;
1899                 } if ((p = strchr(env[i], '='))) {
1900                         if (p == env[i])
1901                                 continue; /* Ignore erroneous entry */
1902                         if (p[-1] == '+')
1903                                 new_env[n++] = env_concat(env[i],
1904                                                           p - env[i] - 1,
1905                                                           find_env(env[i], 1),
1906                                                           p + 1);
1907                         else if (p[1] == '+')
1908                                 new_env[n++] = env_concat(env[i],
1909                                                           p - env[i],
1910                                                           p + 2,
1911                                                           find_env(env[i], 1));
1912 			else if (p[-1] == '?') {
1913 				if (!find_env(env[i], 0))
1914 					new_env[n++] = p + 1;
1915 			} else
1916                                 new_env[n++] = env[i];
1917                 } else {
1918                         p = find_env(env[i], 0);
1919                         if (p)
1920                                 new_env[n++] = p;
1921                 }
1922         }
1923         new_env[n] = NULL;
1924         return new_env;
1925 }
1926 
1927 static int
runas(struct passwd * pw)1928 runas(struct passwd *pw)
1929 {
1930 	gid_t *sgv = NULL;
1931 	size_t sgc = 0, sgm = 0;
1932 	struct group *gr;
1933 
1934 	setgrent();
1935 	while ((gr = getgrent ())) {
1936 		char **p;
1937 		if (gr->gr_gid == pw->pw_gid)
1938 			continue;
1939 		for (p = gr->gr_mem; *p; p++) {
1940 			if (strcmp (*p, pw->pw_name) == 0) {
1941 				if (sgc == sgm)
1942 					sgv = gray_2nrealloc(sgv, &sgm,
1943 							     sizeof(sgv[0]));
1944 				sgv[sgc++] = gr->gr_gid;
1945 			}
1946 		}
1947 	}
1948 	endgrent();
1949 
1950 	if (sgc) {
1951 		if (setgroups(sgc, sgv)) {
1952 			_pam_log(LOG_ERR, "setgroups: %s", strerror(errno));
1953 			free(sgv);
1954 			return 1;
1955 		}
1956 		free(sgv);
1957 	}
1958 
1959 	if (setgid(pw->pw_gid)) {
1960 		_pam_log(LOG_ERR, "setgid(%lu): %s",
1961 			 (unsigned long) pw->pw_gid, strerror(errno));
1962 		return 1;
1963 	}
1964 	if (setuid(pw->pw_uid)) {
1965 		_pam_log(LOG_ERR, "setuid(%lu): %s",
1966 			 (unsigned long) pw->pw_uid, strerror(errno));
1967 		return 1;
1968 	}
1969 	return 0;
1970 }
1971 
1972 static int
run_prog(pam_handle_t * pamh,struct passwd * pw,struct gray_env * env,const char * command,const char * logfile)1973 run_prog(pam_handle_t *pamh, struct passwd *pw, struct gray_env *env,
1974 	 const char *command, const char *logfile)
1975 {
1976 	pid_t pid, rc;
1977 	int p[2];
1978 	long ttl;
1979 	time_t start;
1980 	int i, status;
1981 	struct timeval tv;
1982 	unsigned long timeout_option = 10;
1983 
1984 	DEBUG(2,("running command %s", command));
1985 	get_intval(env, "exec-timeout", 10, &timeout_option);
1986 
1987 	if (pipe(p)) {
1988 		_pam_log(LOG_ERR, "pipe: %s", strerror(errno));
1989 		return PAM_SYSTEM_ERR;
1990 	}
1991 
1992 	pid = fork();
1993 	if (pid == -1) {
1994 		close(p[0]);
1995 		close(p[1]);
1996 		_pam_log(LOG_ERR, "fork: %s", strerror(errno));
1997 		return PAM_SYSTEM_ERR;
1998 	}
1999 
2000 	if (pid == 0) {
2001 		/* child */
2002 		char *argv[3];
2003 
2004 		if (dup2(p[1], 1) == -1) {
2005 			_pam_log(LOG_ERR, "dup2: %s", strerror(errno));
2006 			_exit(127);
2007 		}
2008 		for (i = sysconf(_SC_OPEN_MAX); i >= 0; i--) {
2009 			if (i != 1)
2010 				close(i);
2011 		}
2012 		open("/dev/null", O_RDONLY);
2013 		if (logfile) {
2014 			if (open(logfile, O_CREAT|O_APPEND|O_WRONLY,
2015 				 0644) == -1) {
2016 				_pam_log(LOG_ERR, "open(%s): %s",
2017 					 logfile, strerror(errno));
2018 				_exit(127);
2019 			}
2020 		} else
2021 			dup2(1, 2);
2022 
2023 		if (chdir(pw->pw_dir)) {
2024 			_pam_log(LOG_ERR, "chdir: %s", strerror(errno));
2025 			_exit(127);
2026 		}
2027 
2028 		if (gray_env_get_bool(env, "initrc-root", 0))
2029 			setenv("PAM_LDAPHOME_USER", pw->pw_name, 1);
2030 		else if (runas(pw)) {
2031 			_pam_log(LOG_ERR, "cannot switch to privileges of %s",
2032 				pw->pw_name);
2033 			_exit(127);
2034 		}
2035 
2036 		argv[0] = (char*) command;
2037 		argv[1] = pw->pw_name;
2038 		argv[2] = NULL;
2039 		execve(command, argv,
2040 		       env_setup(gray_env_get(env, "initrc-environ")));
2041 		_exit(127);
2042 	}
2043 
2044 	/* master */
2045 	close(p[1]);
2046 
2047 	start = time(NULL);
2048 	while (1) {
2049 		ttl = timeout_option - (time(NULL) - start);
2050 		if (ttl <= 0) {
2051 			_pam_log(LOG_ERR, "timed out waiting for %s", command);
2052 			break;
2053 		}
2054 		tv.tv_sec = ttl;
2055 		tv.tv_usec = 0;
2056 		rc = select(0, NULL, NULL, NULL, &tv);
2057  		if (rc == -1 && errno == EINTR) {
2058 			rc = waitpid(pid, &status, WNOHANG);
2059 			if (rc == pid)
2060 				break;
2061 			if (rc == (pid_t)-1) {
2062 				_pam_log(LOG_ERR, "waitpid: %s",
2063 					 strerror(errno));
2064 				break;
2065 			}
2066 		}
2067 	}
2068 
2069 	close(p[0]);
2070 
2071 	if (rc != pid) {
2072 		_pam_log(LOG_NOTICE, "killing %s (pid %lu)",
2073 			 command, (unsigned long) pid);
2074 		kill(pid, SIGKILL);
2075 
2076 		while ((rc = waitpid(pid, &status, 0)) == -1 &&
2077 		       errno == EINTR);
2078 		if (rc == (pid_t)-1) {
2079 			_pam_log(LOG_ERR, "waitpid: %s", strerror(errno));
2080 			return PAM_SYSTEM_ERR;
2081 		}
2082 	} else if (WIFEXITED(status)) {
2083 		status = WEXITSTATUS(status);
2084 		if (status) {
2085 			_pam_log(LOG_ERR, "%s exited with status %d",
2086 				 command, status);
2087 			return PAM_SYSTEM_ERR;
2088 		} else
2089 			DEBUG(2,("%s finished successfully", command));
2090 	} else if (WIFSIGNALED(status)) {
2091 		status = WTERMSIG(status);
2092 		_pam_log(LOG_ERR, "%s got signal %d", command, status);
2093 		return PAM_SYSTEM_ERR;
2094 	} else if (status) {
2095 		_pam_log(LOG_ERR, "%s failed: unknown status 0x%x",
2096 			 command, status);
2097 		return PAM_SYSTEM_ERR;
2098 	}
2099 	return PAM_SUCCESS;
2100 }
2101 
2102 static void
sigchld(int sig)2103 sigchld(int sig)
2104 {
2105 	/* nothing */;
2106 }
2107 
2108 static int
run_initrc(pam_handle_t * pamh,struct passwd * pw,struct gray_env * env)2109 run_initrc(pam_handle_t *pamh, struct passwd *pw, struct gray_env *env)
2110 {
2111 	int rc;
2112         struct sigaction sa, save_sa;
2113 	const char *command = gray_env_get(env, "initrc-command");
2114 	const char *logfile = gray_env_get(env, "initrc-log");
2115 
2116 	if (!command)
2117 		return PAM_SUCCESS;
2118 
2119         sigemptyset(&sa.sa_mask);
2120         sa.sa_flags = 0;
2121 	sa.sa_handler = sigchld;
2122 	if (sigaction(SIGCHLD, &sa, &save_sa)) {
2123 		_pam_log(LOG_ERR, "sigaction: %m");
2124 		return PAM_SYSTEM_ERR;
2125 	}
2126 
2127 	rc = run_prog(pamh, pw, env, command, logfile);
2128 
2129 	if (sigaction(SIGCHLD, &save_sa, NULL)) {
2130 		_pam_log(LOG_ERR, "sigaction failed to restore SIGCHLD: %m");
2131 		return PAM_SYSTEM_ERR;
2132 	}
2133 	return rc;
2134 }
2135 
2136 static int
ldaphome_main(pam_handle_t * pamh,int flags,int argc,const char ** argv,const char * func)2137 ldaphome_main(pam_handle_t *pamh, int flags, int argc, const char **argv,
2138 	      const char *func)
2139 {
2140 	int retval = PAM_AUTH_ERR;
2141 	struct gray_env *env;
2142 
2143 	_pam_parse(pamh, argc, argv);
2144 
2145 	DEBUG(90,("enter %s", func));
2146 	gray_pam_init(PAM_AUTHINFO_UNAVAIL);
2147 	if (gray_env_read(config_file_name, &env) == 0) {
2148 		char *val;
2149 		struct passwd *pw;
2150 
2151 		if (val = gray_env_get(env, "ldap-config")) {
2152 			if (strcmp(val, "none") == 0)
2153 				ldap_config_name = NULL;
2154 			else
2155 				ldap_config_name = val;
2156 		}
2157 		if (ldap_config_name) {
2158 			static char *map[] = { "A-Z_", "a-z-" };
2159 			struct gray_env *tmp;
2160 
2161 			gray_env_read_tr(ldap_config_name, &tmp, map);
2162 			gray_env_merge(&env, &tmp);
2163 		}
2164 
2165 		if (val = gray_env_get(env, "authorized_keys"))
2166 			authorized_keys_file = val;
2167 
2168 		if (check_user_groups(pamh, env, &pw, &retval) == 0) {
2169 			switch (create_home_dir(pamh, pw, env)) {
2170 			case create_ok:
2171 				retval = run_initrc(pamh, pw, env);
2172 				if (retval)
2173 					break;
2174 				/* fall through */
2175 			case create_exists:
2176 				retval = import_public_key(pamh, pw, env);
2177 				break;
2178 			case create_failure:
2179 				retval = PAM_SERVICE_ERR;
2180 				break;
2181 			case create_skip:
2182 				retval = PAM_SUCCESS;
2183 			}
2184 		}
2185 		gray_env_free(env);
2186 	}
2187 	DEBUG(90,("exit %s: %d", func, retval));
2188 	return retval;
2189 }
2190 
2191 
2192 
2193 PAM_EXTERN int
pam_sm_authenticate(pam_handle_t * pamh,int flags,int argc,const char ** argv)2194 pam_sm_authenticate(pam_handle_t *pamh, int flags, int argc, const char **argv)
2195 {
2196 	return ldaphome_main(pamh, flags, argc, argv, __FUNCTION__);
2197 }
2198 
2199 PAM_EXTERN int
pam_sm_setcred(pam_handle_t * pamh,int flags,int argc,const char ** argv)2200 pam_sm_setcred(pam_handle_t *pamh, int flags, int argc, const char **argv)
2201 {
2202 	return PAM_SUCCESS;
2203 }
2204 
2205 PAM_EXTERN int
pam_sm_open_session(pam_handle_t * pamh,int flags,int argc,const char ** argv)2206 pam_sm_open_session (pam_handle_t *pamh, int flags, int argc,
2207 		     const char **argv)
2208 {
2209 	return ldaphome_main(pamh, flags, argc, argv, __FUNCTION__);
2210 }
2211 
2212 PAM_EXTERN int
pam_sm_close_session(pam_handle_t * pamh,int flags,int argc,const char ** argv)2213 pam_sm_close_session (pam_handle_t *pamh, int flags, int argc,
2214 		      const char **argv)
2215 {
2216 	return PAM_SUCCESS;
2217 }
2218 
2219 #ifdef PAM_STATIC
2220 
2221 struct pam_module _pam_ldaphome_modstruct = {
2222 	"pam_ldaphome",                      /* name of the module */
2223 	pam_sm_authenticate,
2224 	pam_sm_setcred,
2225 	NULL,
2226 	pam_sm_open_session,
2227 	pam_sm_close_session,
2228 	NULL
2229 };
2230 
2231 #endif
2232 
2233 
2234 
2235 
2236