kerberos_ldap_group.cc
Go to the documentation of this file.
1 /*
2  * Copyright (C) 1996-2023 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"
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 = nullptr;
69  margs->glist = nullptr;
70  margs->llist = nullptr;
71  margs->ulist = nullptr;
72  margs->tlist = nullptr;
73  margs->luser = nullptr;
74  margs->lpass = nullptr;
75  margs->lbind = nullptr;
76  margs->lurl = nullptr;
77  margs->ssl = nullptr;
78  margs->rc_allow = 0;
79  margs->AD = 0;
80  margs->mdepth = 5;
81  margs->nokerberos = 0;
82  margs->ddomain = nullptr;
83  margs->groups = nullptr;
84  margs->ndoms = nullptr;
85  margs->lservs = nullptr;
86  margs->principal = nullptr;
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 = nullptr, *pp = nullptr;
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 = nullptr, *pp = nullptr;
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 = nullptr, *pp = nullptr;
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 = nullptr;
173  }
174  if (margs->ndoms) {
175  clean_nd(margs->ndoms);
176  margs->ndoms = nullptr;
177  }
178  if (margs->lservs) {
179  clean_ls(margs->lservs);
180  margs->lservs = nullptr;
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=nullptr, *dp=nullptr, *np=nullptr;
193  char *nuser, *nuser8 = nullptr, *netbios;
194  int opt;
195  struct main_args margs;
196 #if HAVE_KRB5
197  krb5_error_code code = 0;
198 
199  kparam.context = nullptr;
200 #endif
201 
202  setbuf(stdout, nullptr);
203  setbuf(stdin, nullptr);
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 != nullptr ) {
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]=nullptr;
345  kparam.cc[i]=nullptr;
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) == nullptr) {
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(nullptr, " \n")) != nullptr) {
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 = nullptr;
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
493 {
494  setbuf(stdout, nullptr);
495  setbuf(stdin, nullptr);
496  char buf[6400];
497  while (1) {
498  if (fgets(buf, sizeof(buf) - 1, stdin) == nullptr) {
499  }
500  fprintf(stdout, "ERR\n");
501  fprintf(stderr, "LDAP group authorisation not supported\n");
502  }
503  return EXIT_SUCCESS;
504 }
505 #endif
506 
void clean_args(struct main_args *margs)
char * llist
Definition: support.h:79
char * principal
Definition: support.h:93
int create_nd(struct main_args *margs)
#define PROGRAM
Definition: support.h:169
int mdepth
Definition: support.h:87
char * netbios
Definition: support.h:64
void debug(const char *format,...)
Definition: debug.cc:19
void log(char *format,...)
char * domain
Definition: support.h:70
int check_memberof(struct main_args *margs, char *user, char *domain)
void error(char *format,...)
#define xstrdup
char * optarg
Definition: getopt.c:51
struct gdstruct * groups
Definition: support.h:90
char * luser
Definition: support.h:80
char * ulist
Definition: support.h:76
#define rfc1738_escape(x)
Definition: rfc1738.h:52
int create_gd(struct main_args *margs)
struct lsstruct * next
Definition: support.h:71
int getopt(int nargc, char *const *nargv, const char *ostr)
Definition: getopt.c:62
char * strerror(int ern)
Definition: strerror.c:22
int AD
Definition: support.h:86
char * domain
Definition: support.h:65
struct gdstruct * next
Definition: support.h:61
void init_args(struct main_args *margs)
void rfc1738_unescape(char *url)
Definition: rfc1738.c:146
#define SEND_ERR(x)
int debug_enabled
Definition: debug.cc:13
#define safe_free(x)
Definition: xalloc.h:73
void warn(char *format,...)
#define SEND_BH(x)
char * get_netbios_name(struct main_args *margs, char *netbios)
char * lpass
Definition: support.h:81
const char * LogTime(void)
#define KERBEROS_LDAP_GROUP_VERSION
Definition: support.h:36
int code
Definition: smb-errors.c:145
char * ddomain
Definition: support.h:89
int rc_allow
Definition: support.h:85
char * group
Definition: support.h:59
char * lbind
Definition: support.h:82
int nokerberos
Definition: support.h:88
char * glist
Definition: support.h:75
int log_enabled
char * nlist
Definition: support.h:78
struct lsstruct * lservs
Definition: support.h:92
int create_ls(struct main_args *margs)
char * lserver
Definition: support.h:69
char * ssl
Definition: support.h:84
struct ndstruct * next
Definition: support.h:66
int main()
char * domain
Definition: support.h:60
#define SEND_OK(x)
char * tlist
Definition: support.h:77
char * lurl
Definition: support.h:83
struct ndstruct * ndoms
Definition: support.h:91

 

Introduction

Documentation

Support

Miscellaneous