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