]> git.ipfire.org Git - thirdparty/squid.git/blob - src/acl/external/kerberos_ldap_group/kerberos_ldap_group.cc
Source Format Enforcement (#532)
[thirdparty/squid.git] / src / acl / external / kerberos_ldap_group / kerberos_ldap_group.cc
1 /*
2 * Copyright (C) 1996-2020 The Squid Software Foundation and contributors
3 *
4 * Squid software is distributed under GPLv2+ license and includes
5 * contributions from numerous individuals and organizations.
6 * Please see the COPYING and CONTRIBUTORS files for details.
7 */
8
9 /*
10 * -----------------------------------------------------------------------------
11 *
12 * Author: Markus Moeller (markus_moeller at compuserve.com)
13 *
14 * Copyright (C) 2007 Markus Moeller. All rights reserved.
15 *
16 * This program is free software; you can redistribute it and/or modify
17 * it under the terms of the GNU General Public License as published by
18 * the Free Software Foundation; either version 2 of the License, or
19 * (at your option) any later version.
20 *
21 * This program is distributed in the hope that it will be useful,
22 * but WITHOUT ANY WARRANTY; without even the implied warranty of
23 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
24 * GNU General Public License for more details.
25 *
26 * You should have received a copy of the GNU General Public License
27 * along with this program; if not, write to the Free Software
28 * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307, USA.
29 *
30 * As a special exemption, M Moeller gives permission to link this program
31 * with MIT, Heimdal or other GSS/Kerberos libraries, and distribute
32 * the resulting executable, without including the source code for
33 * the Libraries in the source distribution.
34 *
35 * -----------------------------------------------------------------------------
36 */
37
38 #include "squid.h"
39 #include "helper/protocol_defines.h"
40 #include "rfc1738.h"
41 #include "util.h"
42
43 #if HAVE_LDAP
44
45 #include "support.h"
46 #include <cctype>
47
48 #if HAVE_KRB5
49 struct kstruct kparam;
50
51 #if !HAVE_ERROR_MESSAGE && HAVE_KRB5_GET_ERROR_MESSAGE
52 #define error_message(code) krb5_get_error_message(kparam.context,code)
53 #elif !HAVE_ERROR_MESSAGE && HAVE_KRB5_GET_ERR_TEXT
54 #define error_message(code) krb5_get_err_text(kparam.context,code)
55 #elif !HAVE_ERROR_MESSAGE
56 static char err_code[17];
57 const char *KRB5_CALLCONV
58 error_message(long code) {
59 snprintf(err_code,16,"%ld",code);
60 return err_code;
61 }
62 #endif
63 #endif /* HAVE_KRB5 */
64
65 void
66 init_args(struct main_args *margs)
67 {
68 margs->nlist = NULL;
69 margs->glist = NULL;
70 margs->llist = NULL;
71 margs->ulist = NULL;
72 margs->tlist = NULL;
73 margs->luser = NULL;
74 margs->lpass = NULL;
75 margs->lbind = NULL;
76 margs->lurl = NULL;
77 margs->ssl = NULL;
78 margs->rc_allow = 0;
79 margs->AD = 0;
80 margs->mdepth = 5;
81 margs->nokerberos = 0;
82 margs->ddomain = NULL;
83 margs->groups = NULL;
84 margs->ndoms = NULL;
85 margs->lservs = NULL;
86 margs->principal = NULL;
87 }
88
89 void clean_gd(struct gdstruct *gdsp);
90 void clean_nd(struct ndstruct *ndsp);
91 void clean_ls(struct lsstruct *lssp);
92
93 void
94 clean_gd(struct gdstruct *gdsp)
95 {
96 struct gdstruct *p = NULL, *pp = NULL;
97
98 p = gdsp;
99 while (p) {
100 while (p->next) {
101 pp = p;
102 p = p->next;
103 }
104 safe_free(p->group);
105 safe_free(p->domain);
106 if (pp)
107 safe_free(pp->next);
108 if (p == gdsp)
109 safe_free(gdsp);
110 p = gdsp;
111 }
112 }
113
114 void
115 clean_nd(struct ndstruct *ndsp)
116 {
117 struct ndstruct *p = NULL, *pp = NULL;
118
119 p = ndsp;
120 while (p) {
121 while (p->next) {
122 pp = p;
123 p = p->next;
124 }
125 safe_free(p->netbios);
126 safe_free(p->domain);
127 if (pp)
128 safe_free(pp->next);
129 if (p == ndsp)
130 safe_free(ndsp);
131 p = ndsp;
132 }
133 }
134
135 void
136 clean_ls(struct lsstruct *lssp)
137 {
138 struct lsstruct *p = NULL, *pp = NULL;
139
140 p = lssp;
141 while (p) {
142 while (p->next) {
143 pp = p;
144 p = p->next;
145 }
146 safe_free(p->lserver);
147 safe_free(p->domain);
148 if (pp)
149 safe_free(pp->next);
150 if (p == lssp)
151 safe_free(lssp);
152 p = lssp;
153 }
154 }
155
156 void
157 clean_args(struct main_args *margs)
158 {
159 safe_free(margs->glist);
160 safe_free(margs->ulist);
161 safe_free(margs->tlist);
162 safe_free(margs->nlist);
163 safe_free(margs->llist);
164 safe_free(margs->luser);
165 safe_free(margs->lpass);
166 safe_free(margs->lbind);
167 safe_free(margs->lurl);
168 safe_free(margs->ssl);
169 safe_free(margs->ddomain);
170 if (margs->groups) {
171 clean_gd(margs->groups);
172 margs->groups = NULL;
173 }
174 if (margs->ndoms) {
175 clean_nd(margs->ndoms);
176 margs->ndoms = NULL;
177 }
178 if (margs->lservs) {
179 clean_ls(margs->lservs);
180 margs->lservs = NULL;
181 }
182 safe_free(margs->principal);
183 }
184
185 void strup(char *s);
186
187 int
188 main(int argc, char *const argv[])
189 {
190 char buf[6400];
191 char *user, *domain, *group;
192 char *up=NULL, *dp=NULL, *np=NULL;
193 char *nuser, *nuser8 = NULL, *netbios;
194 int opt;
195 struct main_args margs;
196 #if HAVE_KRB5
197 krb5_error_code code = 0;
198
199 kparam.context = NULL;
200 #endif
201
202 setbuf(stdout, NULL);
203 setbuf(stdin, NULL);
204
205 init_args(&margs);
206
207 while (-1 != (opt = getopt(argc, argv, "diasng:D:N:P:S:u:U:t:T:p:l:b:m:h"))) {
208 switch (opt) {
209 case 'd':
210 debug_enabled = 1;
211 break;
212 case 'i':
213 log_enabled = 1;
214 break;
215 case 'a':
216 margs.rc_allow = 1;
217 break;
218 case 's':
219 margs.ssl = xstrdup("yes");
220 break;
221 case 'n':
222 margs.nokerberos = 1;
223 break;
224 case 'g':
225 margs.glist = xstrdup(optarg);
226 break;
227 case 'D':
228 margs.ddomain = xstrdup(optarg);
229 break;
230 case 'N':
231 margs.nlist = xstrdup(optarg);
232 break;
233 case 'P':
234 margs.principal = xstrdup(optarg);
235 break;
236 case 'u':
237 margs.luser = xstrdup(optarg);
238 break;
239 case 'U':
240 margs.ulist = xstrdup(optarg);
241 break;
242 case 't':
243 margs.ulist = xstrdup(optarg);
244 break;
245 case 'T':
246 margs.tlist = xstrdup(optarg);
247 break;
248 case 'p':
249 margs.lpass = xstrdup(optarg);
250 /* Hide Password */
251 memset(optarg, 'X', strlen(optarg));
252 break;
253 case 'l':
254 margs.lurl = xstrdup(optarg);
255 break;
256 case 'b':
257 margs.lbind = xstrdup(optarg);
258 break;
259 case 'm':
260 margs.mdepth = atoi(optarg);
261 break;
262 case 'S':
263 margs.llist = xstrdup(optarg);
264 break;
265 case 'h':
266 fprintf(stderr, "Usage: \n");
267 fprintf(stderr, "squid_kerb_ldap [-d] [-i] -g group list [-D domain] [-N netbios domain map] [-P service principal name] [-s] [-u ldap user] [-p ldap user password] [-l ldap url] [-b ldap bind path] [-a] [-m max depth] [-h]\n");
268 fprintf(stderr, "-d full debug\n");
269 fprintf(stderr, "-i informational messages\n");
270 fprintf(stderr, "-n do not use Kerberos to authenticate to AD. Requires -u , -p and -l option\n");
271 fprintf(stderr, "-g group list\n");
272 fprintf(stderr, "-t group list (only group name hex UTF-8 format)\n");
273 fprintf(stderr, "-T group list (all in hex UTF-8 format - except separator @)\n");
274 fprintf(stderr, "-D default domain\n");
275 fprintf(stderr, "-N netbios to dns domain map\n");
276 fprintf(stderr, "-P service principal name to be used from keytab\n");
277 fprintf(stderr, "-S ldap server to dns domain map\n");
278 fprintf(stderr, "-u ldap user\n");
279 fprintf(stderr, "-p ldap user password\n");
280 fprintf(stderr, "-l ldap url\n");
281 fprintf(stderr, "-b ldap bind path\n");
282 fprintf(stderr, "-s use SSL encryption with Kerberos authentication\n");
283 fprintf(stderr, "-a allow SSL without cert verification\n");
284 fprintf(stderr, "-m maximal depth for recursive searches\n");
285 fprintf(stderr, "-h help\n");
286 fprintf(stderr, "The ldap url, ldap user and ldap user password details are only used if the kerberised\n");
287 fprintf(stderr, "access fails(e.g. unknown domain) or if the username does not contain a domain part\n");
288 fprintf(stderr, "and no default domain is provided.\n");
289 fprintf(stderr, "If the ldap url starts with ldaps:// it is either start_tls or simple SSL\n");
290 fprintf(stderr, "The group list can be:\n");
291 fprintf(stderr, "group - In this case group can be used for all keberised and non kerberised ldap servers\n");
292 fprintf(stderr, "group@ - In this case group can be used for all keberised ldap servers\n");
293 fprintf(stderr, "group@domain - In this case group can be used for ldap servers of domain domain\n");
294 fprintf(stderr, "group1@domain1:group2@domain2:group3@:group4 - A list is build with a colon as separator\n");
295 fprintf(stderr, "Group membership is determined with AD servers through the users memberof attribute which\n");
296 fprintf(stderr, "is followed to the top (e.g. if the group is a member of a group)\n");
297 fprintf(stderr, "Group membership is determined with non AD servers through the users memberuid (assuming\n");
298 fprintf(stderr, "PosixGroup) or primary group membership (assuming PosixAccount)\n");
299 fprintf(stderr, "The ldap server list can be:\n");
300 fprintf(stderr, "server - In this case server can be used for all Kerberos domains\n");
301 fprintf(stderr, "server@ - In this case server can be used for all Kerberos domains\n");
302 fprintf(stderr, "server@domain - In this case server can be used for Kerberos domain domain\n");
303 fprintf(stderr, "server1a@domain1:server1b@domain1:server2@domain2:server3@:server4 - A list is build with a colon as separator\n");
304 clean_args(&margs);
305 exit(EXIT_SUCCESS);
306 default:
307 warn((char *) "%s| %s: WARNING: unknown option: -%c.\n", LogTime(), PROGRAM, opt);
308 }
309 }
310
311 debug((char *) "%s| %s: INFO: Starting version %s\n", LogTime(), PROGRAM, KERBEROS_LDAP_GROUP_VERSION);
312 int gopt = 0;
313 if (create_gd(&margs)) {
314 if ( margs.glist != NULL ) {
315 debug((char *) "%s| %s: FATAL: Error in group list: %s\n", LogTime(), PROGRAM, margs.glist ? margs.glist : "NULL");
316 SEND_BH("");
317 clean_args(&margs);
318 exit(EXIT_FAILURE);
319 } else {
320 debug((char *) "%s| %s: INFO: no group list given expect it from stdin\n", LogTime(), PROGRAM);
321 gopt = 1;
322 }
323 }
324 if (create_nd(&margs)) {
325 debug((char *) "%s| %s: FATAL: Error in netbios list: %s\n", LogTime(), PROGRAM, margs.nlist ? margs.nlist : "NULL");
326 SEND_BH("");
327 clean_args(&margs);
328 exit(EXIT_FAILURE);
329 }
330 if (create_ls(&margs)) {
331 debug((char *) "%s| %s: Error in ldap server list: %s\n", LogTime(), PROGRAM, margs.llist ? margs.llist : "NULL");
332 SEND_BH("");
333 clean_args(&margs);
334 exit(EXIT_FAILURE);
335 }
336
337 #if HAVE_KRB5
338 /*
339 * Initialise Kerberos
340 */
341
342 code = krb5_init_context(&kparam.context);
343 for (int i=0; i<MAX_DOMAINS; i++) {
344 kparam.mem_ccache[i]=NULL;
345 kparam.cc[i]=NULL;
346 kparam.ncache=0;
347 }
348 if (code) {
349 error((char *) "%s| %s: ERROR: Error while initialising Kerberos library : %s\n", LogTime(), PROGRAM, error_message(code));
350 SEND_BH("");
351 clean_args(&margs);
352 exit(EXIT_FAILURE);
353 }
354 #endif
355
356 while (1) {
357 char *c;
358 if (fgets(buf, sizeof(buf) - 1, stdin) == NULL) {
359 if (ferror(stdin)) {
360 debug((char *) "%s| %s: FATAL: fgets() failed! dying..... errno=%d (%s)\n", LogTime(), PROGRAM, ferror(stdin),
361 strerror(ferror(stdin)));
362
363 SEND_BH(strerror(ferror(stdin)));
364 clean_args(&margs);
365 #if HAVE_KRB5
366 krb5_cleanup();
367 #endif
368 exit(EXIT_FAILURE); /* BIIG buffer */
369 }
370 SEND_BH("fgets NULL");
371 clean_args(&margs);
372 #if HAVE_KRB5
373 krb5_cleanup();
374 #endif
375 exit(EXIT_SUCCESS);
376 }
377 c = (char *) memchr(buf, '\n', sizeof(buf) - 1);
378 if (c) {
379 *c = '\0';
380 } else {
381 SEND_BH("Invalid input. CR missing");
382 debug((char *) "%s| %s: ERR\n", LogTime(), PROGRAM);
383 continue;
384 }
385
386 user = strtok(buf, " \n");
387 if (!user) {
388 debug((char *) "%s| %s: INFO: No Username given\n", LogTime(), PROGRAM);
389 SEND_BH("Invalid request. No Username");
390 continue;
391 }
392 rfc1738_unescape(user);
393 nuser = strchr(user, '\\');
394 if (!nuser)
395 nuser8 = strstr(user, "%5C");
396 if (!nuser && !nuser8)
397 nuser8 = strstr(user, "%5c");
398 domain = strrchr(user, '@');
399 if (nuser || nuser8) {
400 if (nuser) {
401 *nuser = '\0';
402 ++nuser;
403 } else {
404 *nuser8 = '\0';
405 nuser = nuser8 + 3;
406 }
407 netbios = user;
408 up = xstrdup(rfc1738_escape(nuser));
409 np = xstrdup(rfc1738_escape(netbios));
410 if (debug_enabled)
411 debug((char *) "%s| %s: INFO: Got User: %s Netbios Name: %s\n", LogTime(), PROGRAM, up, np);
412 else
413 log((char *) "%s| %s: INFO: Got User: %s Netbios Name: %s\n", LogTime(), PROGRAM, up, np);
414 domain = get_netbios_name(&margs, netbios);
415 user = nuser;
416 safe_free(up);
417 safe_free(np);
418 } else if (domain) {
419 strup(domain);
420 *domain = '\0';
421 ++domain;
422 }
423 up = xstrdup(rfc1738_escape(user));
424 if (domain)
425 dp = xstrdup(rfc1738_escape(domain));
426 if (!domain && margs.ddomain) {
427 domain = xstrdup(margs.ddomain);
428 dp = xstrdup(rfc1738_escape(domain));
429 if (debug_enabled)
430 debug((char *) "%s| %s: INFO: Got User: %s set default domain: %s\n", LogTime(), PROGRAM, up, dp);
431 else
432 log((char *) "%s| %s: INFO: Got User: %s set default domain: %s\n", LogTime(), PROGRAM, up, dp);
433 }
434 if (debug_enabled)
435 debug((char *) "%s| %s: INFO: Got User: %s Domain: %s\n", LogTime(), PROGRAM, up, domain ? dp : "NULL");
436 else
437 log((char *) "%s| %s: INFO: Got User: %s Domain: %s\n", LogTime(), PROGRAM, up, domain ? dp : "NULL");
438
439 safe_free(up);
440 safe_free(dp);
441 if (!strcmp(user, "QQ") && domain && !strcmp(domain, "QQ")) {
442 clean_args(&margs);
443 #if HAVE_KRB5
444 krb5_cleanup();
445 #endif
446
447 exit(EXIT_SUCCESS);
448 }
449 if (gopt) {
450 if ((group = strtok(NULL, " \n")) != NULL) {
451 debug((char *) "%s| %s: INFO: Read group list %s from stdin\n", LogTime(), PROGRAM, group);
452 rfc1738_unescape(group);
453 if (margs.groups) {
454 clean_gd(margs.groups);
455 margs.groups = NULL;
456 }
457 margs.glist = xstrdup(group);
458 if (create_gd(&margs)) {
459 SEND_BH("Error in group list");
460 debug((char *) "%s| %s: FATAL: Error in group list: %s\n", LogTime(), PROGRAM, margs.glist ? margs.glist : "NULL");
461 continue;
462 }
463 } else {
464 SEND_BH("No group list received on stdin");
465 debug((char *) "%s| %s: FATAL: No group list received on stdin\n", LogTime(), PROGRAM);
466 continue;
467 }
468 }
469 if (check_memberof(&margs, user, domain)) {
470 SEND_OK("");
471 debug((char *) "%s| %s: DEBUG: OK\n", LogTime(), PROGRAM);
472 } else {
473 SEND_ERR("");
474 debug((char *) "%s| %s: DEBUG: ERR\n", LogTime(), PROGRAM);
475 }
476 }
477
478 }
479
480 void
481 strup(char *s)
482 {
483 while (*s) {
484 *s = (char)toupper((unsigned char) *s);
485 ++s;
486 }
487 }
488
489 #else
490 #include <cstdlib>
491 int
492 main(int argc, char *const argv[])
493 {
494 setbuf(stdout, NULL);
495 setbuf(stdin, NULL);
496 char buf[6400];
497 while (1) {
498 if (fgets(buf, sizeof(buf) - 1, stdin) == NULL) {
499 }
500 fprintf(stdout, "ERR\n");
501 fprintf(stderr, "LDAP group authorisation not supported\n");
502 }
503 return EXIT_SUCCESS;
504 }
505 #endif
506