1 /*
2 * Copyright (c) 1997, 1998, 1999, 2000, 2001, 2002, 2003, 2005, 2006, 2008,
3 * 2009, 2010, 2011, 2012, 2013, 2019, 2020, 2021
4 * Inferno Nettverk A/S, Norway. All rights reserved.
5 *
6 * Redistribution and use in source and binary forms, with or without
7 * modification, are permitted provided that the following conditions
8 * are met:
9 * 1. The above copyright notice, this list of conditions and the following
10 * disclaimer must appear in all copies of the software, derivative works
11 * or modified versions, and any portions thereof, aswell as in all
12 * supporting documentation.
13 * 2. All advertising materials mentioning features or use of this software
14 * must display the following acknowledgement:
15 * This product includes software developed by
16 * Inferno Nettverk A/S, Norway.
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 BY THE AUTHOR ``AS IS'' AND ANY EXPRESS OR
21 * IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES
22 * OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED.
23 * IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR ANY DIRECT, INDIRECT,
24 * INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT
25 * NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE,
26 * DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY
27 * THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
28 * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF
29 * THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
30 *
31 * Inferno Nettverk A/S requests users of this software to return to
32 *
33 * Software Distribution Coordinator or sdc@inet.no
34 * Inferno Nettverk A/S
35 * Oslo Research Park
36 * Gaustadall�en 21
37 * NO-0349 Oslo
38 * Norway
39 *
40 * any improvements or extensions that they make and grant Inferno Nettverk A/S
41 * the rights to redistribute these changes.
42 *
43 */
44
45 #include "common.h"
46
47 static const char rcsid[] =
48 "$Id: accesscheck.c,v 1.89.10.13 2021/03/24 23:06:06 karls Exp $";
49
50 int
usermatch(auth,userlist)51 usermatch(auth, userlist)
52 const authmethod_t *auth;
53 const linkedname_t *userlist;
54 {
55 /* const char *function = "usermatch()"; */
56 const char *name;
57
58 if ((name = authname(auth)) == NULL)
59 return 0; /* no username, no match. */
60
61 do
62 if (strcmp(name, userlist->name) == 0)
63 break;
64 while ((userlist = userlist->next) != NULL);
65
66 if (userlist == NULL)
67 return 0; /* no match. */
68 return 1;
69 }
70
71 int
groupmatch(auth,grouplist)72 groupmatch(auth, grouplist)
73 const authmethod_t *auth;
74 const linkedname_t *grouplist;
75 {
76 const char *function = "groupmatch()";
77 const char *username;
78 struct passwd *pw;
79 struct group *groupent;
80
81 SASSERTX(grouplist != NULL);
82
83 if ((username = authname(auth)) == NULL)
84 return 0; /* no username, no match. */
85
86 /*
87 * First check the primary group of the user against grouplist.
88 * If the groupname given there matches, we don't need to go through
89 * all users in the list of group.
90 */
91 if ((pw = getpwnam(username)) != NULL
92 && (groupent = getgrgid(pw->pw_gid)) != NULL) {
93 const linkedname_t *listent = grouplist;
94
95 do
96 if (strcmp(groupent->gr_name, listent->name) == 0)
97 return 1;
98 while ((listent = listent->next) != NULL);
99 }
100 else {
101 if (pw == NULL)
102 slog(LOG_DEBUG, "%s: unknown username \"%s\"", function, username);
103 else if (groupent == NULL)
104 slog(LOG_DEBUG, "%s: unknown primary groupid %ld",
105 function, (long)pw->pw_gid);
106 }
107
108 /*
109 * Go through grouplist, matching username against each groupmember of
110 * all the groups in grouplist.
111 */
112 do {
113 char **groupname;
114
115 if ((groupent = getgrnam(grouplist->name)) == NULL) {
116 swarn("%s: unknown groupname \"%s\"", function, grouplist->name);
117 continue;
118 }
119
120 groupname = groupent->gr_mem;
121
122 while (*groupname != NULL) {
123 if (strcmp(username, *groupname) == 0)
124 return 1; /* match. */
125
126 ++groupname;
127 }
128 } while ((grouplist = grouplist->next) != NULL);
129
130 return 0;
131 }
132
133 #if HAVE_LDAP
134 int
ldapgroupmatch(auth,rule)135 ldapgroupmatch(auth, rule)
136 const authmethod_t *auth;
137 const rule_t *rule;
138 {
139 const char *function = "ldapgroupmatch()";
140 const linkedname_t *grouplist;
141 const char *username;
142 char *userdomain;
143 int retval;
144
145 if ((username = authname(auth)) == NULL)
146 return 0; /* no username, no match. */
147
148 #if !HAVE_GSSAPI
149
150 if (!rule->state.ldapauthorisation.ldapurl)
151 SERRX(rule->state.ldapauthorisation.ldapurl != NULL);
152
153 #endif /* !HAVE_GSSAPI */
154
155 if ((userdomain = strchr(username, '@')) != NULL)
156 ++userdomain;
157
158 if (userdomain == NULL && *rule->state.ldapauthorisation.domain == NUL
159 && rule->state.ldapauthorisation.ldapurl == NULL) {
160 slog(LOG_WARNING,
161 "%s: cannot check ldap group membership for user %s: username has no "
162 "domain postfix and no ldap.domain value is set",
163 function, username);
164
165 return 0;
166 }
167
168 if ((retval = ldap_user_is_cached(username, rule->number)) >= 0)
169 return retval;
170
171 /*
172 * go through grouplist, matching username against members of each group.
173 */
174
175 grouplist = rule->ldapgroup;
176 do {
177 char *groupdomain;
178 char groupname[MAXNAMELEN];
179
180 slog(LOG_DEBUG, "%s: checking if user %s is member of ldap group %s",
181 function, username, grouplist->name);
182
183 STRCPY_ASSERTLEN(groupname, grouplist->name);
184
185 if ((groupdomain = strchr(groupname, '@')) != NULL) {
186 *groupdomain = NUL; /* separates groupname from groupdomain. */
187 ++groupdomain;
188 }
189
190 if (groupdomain != NULL && userdomain != NULL) {
191 if (strcmp(groupdomain, userdomain) != 0
192 && strcmp(groupdomain, "") != 0) {
193 slog(LOG_DEBUG,
194 "%s: userdomain \"%s\" does not match groupdomain "
195 "\"%s\" and groupdomain is not default domain. "
196 "Trying next entry",
197 function, userdomain, groupdomain);
198
199 continue;
200 }
201 }
202
203 if (ldapgroupmatches(auth,
204 username,
205 userdomain,
206 groupname,
207 groupdomain,
208 rule)) {
209 cache_ldap_user(username, 1, rule->number);
210 return 1;
211 }
212 } while ((grouplist = grouplist->next) != NULL);
213
214 cache_ldap_user(username, 0, rule->number);
215 return 0;
216 }
217
218 #endif /* HAVE_LDAP */
219
220 #if HAVE_PAC
221 int
sidmatch(auth,objectsids)222 sidmatch(auth, objectsids)
223 const authmethod_t *auth;
224 const linkedname_t *objectsids;
225 {
226 const char *function = "sidmatch()";
227 const linkedname_t *grouplist;
228
229 if (authsids(auth) == NULL) {
230 slog(LOG_DEBUG, "%s: no pac sids found", function);
231
232 return 0; /* no username, no match. */
233 }
234
235 /* go through grouplist, matching username against members of each group. */
236 grouplist = objectsids;
237 do {
238 char sids[strlen((const char *)authsids(auth)) + 1];
239 char groupname[MAXNAMELEN];
240 char tsid[MAX_BASE64_LEN];
241 char *token;
242 int convres;
243
244 STRCPY_ASSERTLEN(sids, authsids(auth));
245
246 slog(LOG_DEBUG, "%s: checking if a sid in pac sids %s matches %s",
247 function, sids, grouplist->name);
248
249 STRCPY_ASSERTLEN(groupname, grouplist->name);
250
251 token = strtok(sids, " ");
252 while (token) {
253 if (!strcmp(token, grouplist->name)) {
254 if ((convres = b64tosid(token, tsid, sizeof(tsid))) == 0)
255 slog(LOG_DEBUG, "%s: user sid %s matches group sid %s/%s",
256 function, token, tsid, grouplist->name);
257 else {
258 slog(LOG_DEBUG, "%s: user sid %s matches group sid %s",
259 function, token, grouplist->name);
260 #if DIAGNOSTIC
261 SERRX(convres);
262 #endif /* DEBUG */
263 }
264
265 return 1;
266 }
267
268 token = strtok(NULL, " ");
269 }
270
271 if ((convres = b64tosid(grouplist->name, tsid, sizeof(tsid))) == 0)
272 slog(LOG_DEBUG, "%s: user sids do not match group sid %s/%s",
273 function, tsid, grouplist->name);
274 else {
275 slog(LOG_DEBUG, "%s: user sids do not match group sid %s",
276 function, grouplist->name);
277
278 #if DIAGNOSTIC
279 SERRX(convres);
280 #endif /* DEBUG */
281 }
282
283 } while ((grouplist = grouplist->next) != NULL);
284
285 slog(LOG_DEBUG, "%s: user sids do not match any group", function);
286 return 0;
287 }
288 #endif /* HAVE_PAC */
289
290 int
accesscheck(s,auth,src,dst,emsg,emsgsize)291 accesscheck(s, auth, src, dst, emsg, emsgsize)
292 int s;
293 authmethod_t *auth;
294 const struct sockaddr_storage *src, *dst;
295 char *emsg;
296 size_t emsgsize;
297 {
298 const char *function = "accesscheck()";
299 char srcstr[MAXSOCKADDRSTRING], dststr[sizeof(srcstr)];
300 int match, authresultisfixed;
301
302 if (sockscf.option.debug)
303 slog(LOG_DEBUG, "%s: method: %s, %s -> %s ",
304 function,
305 method2string(auth->method),
306 src == NULL ?
307 "<unknown>" : sockaddr2string(src, srcstr, sizeof(srcstr)),
308 dst == NULL ?
309 "<unknown>" : sockaddr2string(dst, dststr, sizeof(dststr)));
310
311 /*
312 * We don't want to re-check the same method. This could
313 * happen in several cases:
314 * - was checked as client-rule, is now checked as socks-rule.
315 * - a different rule with the same method. The client is however
316 * the same, so if the auth for method 'k' failed in previous rule,
317 * it will fail the next time also.
318 */
319
320 if (methodisset(auth->method, auth->methodv, (size_t)auth->methodc)) {
321 slog(LOG_DEBUG, "%s: method %s already checked, matches",
322 function, method2string(auth->method));
323
324 return 1; /* already checked, matches. */
325 }
326
327 if (methodisset(auth->method, auth->badmethodv, (size_t)auth->badmethodc)) {
328 snprintf(emsg, emsgsize,
329 "authentication provided by client for method \"%s\" "
330 "does not match",
331 method2string(auth->method));
332
333 slog(LOG_DEBUG, "%s: method %s already checked, does not match",
334 function, method2string(auth->method));
335
336 return 0; /* already checked, won't match. */
337 }
338
339 match = 0;
340
341 switch (auth->method) {
342 /*
343 * Methods where no further checking is done at this point, either
344 * because there's nothing to check, or it has already been checked.
345 */
346 case AUTHMETHOD_NONE:
347 #if HAVE_GSSAPI
348 case AUTHMETHOD_GSSAPI:
349 #endif /* HAVE_GSSAPI */
350 match = 1;
351 break;
352
353 case AUTHMETHOD_UNAME:
354 if (passwordcheck((const char *)auth->mdata.uname.name,
355 (const char *)auth->mdata.uname.password,
356 emsg,
357 emsgsize) == 0)
358 match = 1;
359
360 break;
361
362 #if HAVE_LIBWRAP
363 case AUTHMETHOD_RFC931:
364 if (passwordcheck((const char *)auth->mdata.rfc931.name,
365 NULL,
366 emsg,
367 emsgsize) == 0)
368 match = 1;
369 break;
370 #endif /* HAVE_LIBWRAP */
371
372 #if HAVE_PAM
373 case AUTHMETHOD_PAM_ANY:
374 case AUTHMETHOD_PAM_ADDRESS:
375 case AUTHMETHOD_PAM_USERNAME: {
376 #if DIAGNOSTIC
377 const int freec
378 = freedescriptors(sockscf.option.debug ? "start" : NULL, NULL);
379 #endif /* DIAGNOSTIC */
380
381 if (pam_passwordcheck(s,
382 src,
383 dst,
384 &auth->mdata.pam,
385 emsg,
386 emsgsize) == 0)
387 match = 1;
388
389 #if DIAGNOSTIC
390 if (freec
391 != freedescriptors(sockscf.option.debug ? "end" : NULL, NULL))
392 swarnx("%s: lost %d file descriptor%s in pam_passwordcheck()",
393 function,
394 freec - freedescriptors(NULL, NULL),
395 freec - freedescriptors(NULL, NULL) == 1 ? "" : "s");
396 #endif /* DIAGNOSTIC */
397
398 break;
399 }
400 #endif /* HAVE_PAM */
401
402 #if HAVE_BSDAUTH
403 case AUTHMETHOD_BSDAUTH: {
404 #if DIAGNOSTIC
405 const int freec
406 = freedescriptors(sockscf.option.debug ? "start" : NULL, NULL);
407 #endif /* DIAGNOSTIC */
408
409 if (bsdauth_passwordcheck(s,
410 src,
411 dst,
412 &auth->mdata.bsd,
413 emsg,
414 emsgsize) == 0)
415 match = 1;
416
417 #if DIAGNOSTIC
418 if (freec
419 != freedescriptors(sockscf.option.debug ? "end" : NULL, NULL))
420 swarnx("%s: lost %d file descriptor%s in bsdauth_passwordcheck()",
421 function,
422 freec - freedescriptors(NULL, NULL),
423 freec - freedescriptors(NULL, NULL) == 1 ? "" : "s");
424 #endif /* DIAGNOSTIC */
425 break;
426 }
427 #endif /* HAVE_BSDAUTH */
428
429 #if HAVE_LDAP
430
431 case AUTHMETHOD_LDAPAUTH: {
432
433 #if DIAGNOSTIC
434
435 const int freec
436
437 = freedescriptors(sockscf.option.debug ? "start" : NULL, NULL);
438 #endif /* DIAGNOSTIC */
439
440 /*
441 * Temporary workaround in Dante 1.4.x for LDAP library performing
442 * slow systemcalls (e.g. connect(2)) that may get interrupted
443 * if admin sends us e.g. frequent SIGINFO-signals. The LDAP
444 * library does not retry if e.g. its connect(2) gets interrupted,
445 * so to avoid problems, block our own signals while executing the
446 * ldap-code. Better workaround expected for Dante 1.5.x.
447 */
448 sigset_t oldmask, newmask;
449 int signalswereblocked;
450
451 sigemptyset(&newmask);
452 sigaddset(&newmask, SIGHUP);
453 sigaddset(&newmask, SIGUSR1);
454
455 #if HAVE_SIGNAL_SIGINFO
456
457 sigaddset(&newmask, SIGINFO);
458
459 #endif /* HAVE_SIGNAL_SIGINFO */
460
461 if (sigprocmask(SIG_BLOCK, &newmask, &oldmask) == 0)
462 signalswereblocked = 1;
463 else {
464 swarn("%s: sigprocmask(SIG_BLOCK)", function);
465 signalswereblocked = 0;
466 }
467
468 if (ldapauth_passwordcheck(s,
469 src,
470 dst,
471 &auth->mdata.ldap,
472 emsg,
473 emsgsize) == 0)
474 match = 1;
475
476 if (signalswereblocked)
477 if (sigprocmask(SIG_SETMASK, &oldmask, NULL) != 0)
478 swarn("%s: sigprocmask(SIG_SETMASK)", function);
479
480 #if DIAGNOSTIC
481
482 if (freec
483 != freedescriptors(sockscf.option.debug ? "end" : NULL, NULL))
484 swarnx("%s: lost %d file descriptor%s in ldapauth_passwordcheck()",
485 function,
486 freec - freedescriptors(NULL, NULL),
487 freec - freedescriptors(NULL, NULL) == 1 ? "" : "s");
488
489 #endif /* DIAGNOSTIC */
490
491 break;
492 }
493
494 #endif /* HAVE_LDAP */
495
496 default:
497 SERRX(auth->method);
498 }
499
500 /*
501 * Some methods can be called with different values for the
502 * same client, based on values configured in the rules.
503 * Others cannot and we want to mark those that cannot as
504 * "tried", so we don't waste time on re-trying them.
505 */
506 switch (auth->method) {
507
508 #if HAVE_PAM
509
510 case AUTHMETHOD_PAM_ANY:
511 case AUTHMETHOD_PAM_ADDRESS:
512 case AUTHMETHOD_PAM_USERNAME:
513 if (*sockscf.state.pamservicename == NUL)
514 authresultisfixed = 0;
515 else
516 authresultisfixed = 1;
517
518 break;
519
520 #endif /* HAVE_PAM */
521
522 #if HAVE_BSDAUTH
523
524 case AUTHMETHOD_BSDAUTH:
525 if (sockscf.state.bsdauthstylename == NULL)
526 authresultisfixed = 0;
527 else
528 authresultisfixed = 1;
529
530 break;
531
532 #endif /* HAVE_BSDAUTH */
533
534 #if HAVE_LDAP
535
536 case AUTHMETHOD_LDAPAUTH:
537 if (sockscf.state.ldapauthentication.ldapurl == NULL)
538 authresultisfixed = 0;
539 else
540 authresultisfixed = 1;
541
542 break;
543
544 #endif /* HAVE_LDAP */
545
546 #if HAVE_GSSAPI
547
548 case AUTHMETHOD_GSSAPI:
549 if (*sockscf.state.gssapiservicename == NUL
550 || *sockscf.state.gssapikeytab == NUL)
551 authresultisfixed = 0;
552 else
553 authresultisfixed = 1;
554
555 break;
556
557 #endif /* HAVE_GSSAPI */
558
559 case AUTHMETHOD_NONE:
560 case AUTHMETHOD_UNAME:
561 case AUTHMETHOD_RFC931:
562 authresultisfixed = 1;
563 break;
564
565 default:
566 SERRX(auth->method);
567 }
568
569 if (authresultisfixed) {
570 if (match) {
571 SASSERTX(auth->methodc + 1 <= ELEMENTS(auth->methodv));
572 auth->methodv[auth->methodc++] = auth->method;
573 }
574 else {
575 SASSERTX(auth->badmethodc + 1 <= ELEMENTS(auth->badmethodv));
576 auth->badmethodv[auth->badmethodc++] = auth->method;
577 }
578
579 /*
580 * Unfortunately we can not bzero() the password for several reasons:
581 * 1) If UDP, perhaps packets to different targets will require different
582 * authentication. E.g. username was used when establishing the
583 * control-connection, while pam is used when sending packets to
584 * certain targets. Unlikely, but not impossible.
585 *
586 * 2) If we are forwarding to an upstream proxy that we are
587 * configured to offer the username/password from the user.
588 */
589 }
590
591 if (match)
592 slog(LOG_DEBUG, "%s: authentication matched", function);
593 else
594 slog(LOG_DEBUG, "%s: no match for authentication%s %s",
595 function,
596 emsgsize > 0 ? ":" : "",
597 emsgsize > 0 ? emsg : "");
598
599 return match;
600 }
601