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