xref: /openbsd-src/libexec/login_ldap/login_ldap.c (revision 06971c39b9a02fc25c5f53091460ad9f1b768328)
1 /*
2  * $OpenBSD: login_ldap.c,v 1.3 2021/09/02 20:57:58 deraadt Exp $
3  * Copyright (c) 2002 Institute for Open Systems Technology Australia (IFOST)
4  * Copyright (c) 2007 Michael Erdely <merdely@openbsd.org>
5  * Copyright (c) 2019 Martijn van Duren <martijn@openbsd.org>
6  *
7  * All rights reserved.
8  *
9  * Redistribution and use in source and binary forms, with or without
10  * modification, are permitted provided that the following conditions
11  * are met:
12  * 1. Redistributions of source code must retain the above copyright
13  *    notice, this list of conditions and the following disclaimer.
14  * 2. Redistributions in binary form must reproduce the above copyright
15  *    notice, this list of conditions and the following disclaimer in the
16  *    documentation and/or other materials provided with the distribution.
17  * 3. The name of the author may not be used to endorse or promote products
18  *    derived from this software without specific prior written permission.
19  *
20  * THIS SOFTWARE IS PROVIDED ``AS IS'' AND ANY EXPRESS OR IMPLIED WARRANTIES,
21  * INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY
22  * AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED.  IN NO EVENT SHALL
23  * THE AUTHOR BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL,
24  * EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO,
25  * PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS;
26  * OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY,
27  * WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR
28  * OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF
29  * ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
30  */
31 
32 #include <sys/time.h>
33 #include <sys/resource.h>
34 #include <netinet/in.h>
35 
36 #include <errno.h>
37 #include <signal.h>
38 #include <stdio.h>
39 #include <stdlib.h>
40 #include <string.h>
41 #include <syslog.h>
42 #include <limits.h>
43 #include <unistd.h>
44 #include <login_cap.h>
45 #include <stdarg.h>
46 #include <bsd_auth.h>
47 
48 #include "aldap.h"
49 #include "login_ldap.h"
50 
51 int	auth_ldap(char *, char *, char *);
52 __dead void usage(void);
53 
54 static void
handler(int signo)55 handler(int signo)
56 {
57 	_exit(1);
58 }
59 
60 /*
61  * Authenticate a user using LDAP
62  */
63 
64 int
main(int argc,char ** argv)65 main(int argc, char **argv)
66 {
67 	int c;
68 	char *class, *service, *username, *password;
69 	char backbuf[BUFSIZ];
70 	FILE *back = NULL;
71 
72 	password = class = NULL;
73 	service = LOGIN_DEFSERVICE;
74 
75 	(void)signal(SIGQUIT, SIG_IGN);
76 	(void)signal(SIGINT, SIG_IGN);
77 	(void)signal(SIGALRM, handler);
78 	(void)setpriority(PRIO_PROCESS, 0, 0);
79 
80 	openlog("login_ldap", LOG_ODELAY, LOG_AUTH);
81 
82 	/*
83 	 * Usage: login_xxx [-s service] [-v var] user [class]
84 	 */
85 	while ((c = getopt(argc, argv, "ds:v:")) != -1) {
86 		switch(c) {
87 		case 'd':
88 			back = stdout;
89 			debug = 1;
90 			break;
91 		case 'v':
92 			/* For compatibility */
93 			break;
94 		case 's':       /* service */
95 			service = optarg;
96 			if (strcmp(service, "login") != 0 &&
97 				strcmp(service, "challenge") != 0 &&
98 				strcmp(service, "response") != 0) {
99 					dlog(0, "%s not supported", service);
100 					return 1;
101 				}
102 			break;
103 		default:
104 			usage();
105 		}
106 	}
107 
108 	argc -= optind;
109 	argv += optind;
110 
111 	switch (argc) {
112 	case 2:
113 		class = argv[1];
114 		/* FALLTHROUGH */
115 	case 1:
116 		username = argv[0];
117 		break;
118 	default:
119 		usage();
120 	}
121 
122 	if (strlen(username) >= LOGIN_NAME_MAX) {
123 		dlog(0, "username too long");
124 		return 1;
125 	}
126 
127 	/*
128 	 * Filedes 3 is the back channel, where we send back results.
129 	 */
130 	if (back == NULL && (back = fdopen(3, "a")) == NULL)  {
131 		dlog(0, "error reopening back channel");
132 		return 1;
133 	}
134 
135 	if (strcmp(service, "login") == 0) {
136 		password = getpass("Password: ");
137 	} else if (strcmp(service, "response") == 0) {
138 		int n;
139 
140 		/*
141 		 * format of back channel message
142 		 *
143 		 * c h a l l e n g e \0 p a s s w o r d \0
144 		 *
145 		 * challenge can be NULL (so can password too
146 		 * i suppose).
147 		 */
148 
149 		n = read(3, backbuf, sizeof(backbuf));
150 		if (n == -1) {
151 			dlog(0, "read error from back channel");
152 			return 1;
153 		}
154 
155 		/* null challenge */
156 		if (backbuf[0] == '\0')
157 			password = backbuf + 1;
158 		/* skip the first string to get the password */
159 		else if ((password = strchr(backbuf, '\0')) != NULL)
160 			password++;
161 		else
162 			dlog(0, "protocol error on back channel");
163 
164 	} else if (strcmp(service, "challenge") == 0) {
165 		(void)fprintf(back, BI_SILENT "\n");
166 		return 0;
167 	}
168 
169 	if (password == NULL || password[0] == '\0') {
170 		dlog(1, "No password specified");
171 		(void)fprintf(back, BI_REJECT "\n");
172 		return 1;
173 	}
174 
175 	if (auth_ldap(username, class, password))
176 		(void)fprintf(back, BI_AUTH "\n");
177 	else
178 		(void)fprintf(back, BI_REJECT "\n");
179 
180 	explicit_bzero(password, strlen(password));
181 
182 	closelog();
183 
184 	return 0;
185 }
186 
187 int
auth_ldap(char * user,char * class,char * pass)188 auth_ldap(char *user, char *class, char *pass)
189 {
190 	struct auth_ctx ctx;
191 	const char *cfile = NULL;
192 	char *tmp;
193 	login_cap_t	*lc;
194 
195 	memset(&ctx, 0, sizeof(struct auth_ctx));
196 	TAILQ_INIT(&(ctx.s));
197 
198 	if ((lc = login_getclass(class)) != NULL) {
199 		cfile = login_getcapstr(lc, "ldap-conffile", NULL, NULL);
200 	}
201 
202 	ctx.user = user;
203 
204 	if (!parse_conf(&ctx, cfile != NULL ? cfile : "/etc/login_ldap.conf"))
205 		return 0;
206 
207 
208 	if (!conn(&ctx)) {
209 		dlog(0, "ldap_open failed");
210 		return 0;
211 	}
212 
213 	if (ctx.basedn == NULL) {
214 		if (!bind_password(&ctx, ctx.binddn, pass))
215 			return 0;
216 		ctx.userdn = ctx.binddn;
217 	} else {
218 		if (!bind_password(&ctx, ctx.binddn, ctx.bindpw))
219 			return 0;
220 
221 		dlog(1, "bind success!");
222 
223 		ctx.userdn = search(&ctx, ctx.basedn, ctx.filter, ctx.scope);
224 		if (ctx.userdn == NULL) {
225 			dlog(1, "no user!");
226 			return 0;
227 		}
228 
229 	}
230 	dlog(1, "userdn %s", ctx.userdn);
231 
232 	if (ctx.gbasedn != NULL && ctx.gfilter != NULL) {
233 		tmp = ctx.gfilter;
234 		if ((ctx.gfilter = parse_filter(&ctx, ctx.gfilter)) == NULL)
235 			return 0;
236 		free(tmp);
237 		tmp = ctx.gbasedn;
238 		if ((ctx.gbasedn = parse_filter(&ctx, ctx.gbasedn)) == NULL)
239 			return 0;
240 		free(tmp);
241 
242 		if (search(&ctx, ctx.gbasedn, ctx.gfilter, ctx.gscope) == NULL) {
243 			dlog(0, "user authenticated but failed group check: "
244 			    "%s", ctx.gfilter);
245 			return 0;
246 		}
247 		dlog(1, "group filter matched!");
248 	}
249 
250 
251 	if (ctx.basedn != NULL) {
252 		if (!bind_password(&ctx, ctx.userdn, pass)) {
253 			dlog(0, "user bind failed, dn: %s", ctx.userdn);
254 			return 0;
255 		}
256 	}
257 
258 	dlog(1, "user bind success!");
259 
260 	return 1;
261 }
262 
263 __dead void
usage(void)264 usage(void)
265 {
266 	dlog(0, "usage: %s [-d] [-s service] [-v name=value] user [class]", getprogname());
267 	exit(1);
268 }
269