1 /*
2 Authors:
3 Sumit Bose <sbose@redhat.com>
4
5 Copyright (C) 2009 Red Hat
6
7 This program is free software; you can redistribute it and/or modify
8 it under the terms of the GNU General Public License as published by
9 the Free Software Foundation; either version 3 of the License, or
10 (at your option) any later version.
11
12 This program is distributed in the hope that it will be useful,
13 but WITHOUT ANY WARRANTY; without even the implied warranty of
14 MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
15 GNU General Public License for more details.
16
17 You should have received a copy of the GNU General Public License
18 along with this program. If not, see <http://www.gnu.org/licenses/>.
19 */
20 #define _GNU_SOURCE
21 #include <sys/socket.h>
22 #include <netinet/in.h>
23 #include <arpa/inet.h>
24 #include <stdio.h>
25 #include <string.h>
26 #include <stdlib.h>
27 #include <errno.h>
28 #include <sys/types.h>
29 #include <netdb.h>
30 #include <sys/stat.h>
31 #include <fcntl.h>
32 #include <ctype.h>
33
34 #include <krb5/locate_plugin.h>
35
36 #include "providers/krb5/krb5_common.h"
37
38 #define DEFAULT_KERBEROS_PORT 88
39 #define DEFAULT_KADMIN_PORT 749
40 #define DEFAULT_KPASSWD_PORT 464
41
42 #define BUFSIZE 512
43 #define PORT_STR_SIZE 7
44 #define SSSD_KRB5_LOCATOR_DEBUG "SSSD_KRB5_LOCATOR_DEBUG"
45 #define DEBUG_KEY "[sssd_krb5_locator] "
46 #define PLUGIN_DEBUG(body) do { \
47 if (ctx->debug) { \
48 debug_fn body; \
49 } \
50 } while(0);
51
52 struct sssd_ctx {
53 char *sssd_realm;
54 char *kdc_addr;
55 uint16_t kdc_port;
56 char *kpasswd_addr;
57 uint16_t kpasswd_port;
58 bool debug;
59 };
60
61 void debug_fn(const char *format, ...)
62 {
63 va_list ap;
64 char *s = NULL;
65 int ret;
66
Event va_init: Initializing va_list "ap".
Also see events: [missing_va_end] | |
67 va_start(ap, format);
68
69 ret = vasprintf(&s, format, ap);
At conditional (1): "ret < 0": Taking true branch.
|
70 if (ret < 0) {
71 /* ENOMEM */
Event missing_va_end: va_end was not called for "ap".
Also see events: [va_init] | |
72 return;
73 }
74
75 va_end(ap);
76
77 fprintf(stderr, DEBUG_KEY "%s", s);
78 free(s);
79 }
80
81 static int get_krb5info(const char *realm, struct sssd_ctx *ctx,
82 enum locate_service_type svc)
83 {
84 int ret;
85 char *krb5info_name = NULL;
86 size_t len;
87 uint8_t buf[BUFSIZE + 1];
88 uint8_t *p;
89 int fd = -1;
90 const char *name_tmpl = NULL;
91 char *port_str;
92 long port;
93 char *endptr;
94
95 switch (svc) {
96 case locate_service_kdc:
97 name_tmpl = KDCINFO_TMPL;
98 break;
99 case locate_service_kpasswd:
100 name_tmpl = KPASSWDINFO_TMPL;
101 break;
102 default:
103 PLUGIN_DEBUG(("Unsupported service [%d].\n", svc));
104 return EINVAL;
105 }
106
107
108 len = strlen(realm) + strlen(name_tmpl);
109
110 krb5info_name = calloc(1, len + 1);
111 if (krb5info_name == NULL) {
112 PLUGIN_DEBUG(("malloc failed.\n"));
113 return ENOMEM;
114 }
115
116 ret = snprintf(krb5info_name, len, name_tmpl, realm);
117 if (ret < 0) {
118 PLUGIN_DEBUG(("snprintf failed.\n"));
119 ret = EINVAL;
120 goto done;
121 }
122 krb5info_name[len] = '\0';
123
124 fd = open(krb5info_name, O_RDONLY);
125 if (fd == -1) {
126 PLUGIN_DEBUG(("open failed [%d][%s].\n", errno, strerror(errno)));
127 ret = errno;
128 goto done;
129 }
130
131 len = BUFSIZE;
132 p = buf;
133 memset(buf, 0, BUFSIZE+1);
134 while (len != 0 && (ret = read(fd, p, len)) != 0) {
135 if (ret == -1) {
136 if (errno == EINTR) continue;
137 PLUGIN_DEBUG(("read failed [%d][%s].\n", errno, strerror(errno)));
138 close(fd);
139 goto done;
140 }
141
142 len -= ret;
143 p += ret;
144 }
145 close(fd);
146
147 if (len == 0) {
148 PLUGIN_DEBUG(("Content of krb5info file [%s] is [%d] or larger.\n",
149 krb5info_name, BUFSIZE));
150 }
151 PLUGIN_DEBUG(("Found [%s] in [%s].\n", buf, krb5info_name));
152
153 port_str = strrchr((char *) buf, ':');
154 if (port_str == NULL) {
155 port = 0;
156 } else {
157 *port_str = '\0';
158 ++port_str;
159
160 if (isdigit(*port_str)) {
161 errno = 0;
162 port = strtol(port_str, &endptr, 10);
163 if (errno != 0) {
164 ret = errno;
165 PLUGIN_DEBUG(("strtol failed on [%s]: [%d][%s], "
166 "assuming default.\n", port_str, ret, strerror(ret)));
167 port = 0;
168 }
169 if (*endptr != '\0') {
170 PLUGIN_DEBUG(("Found additional characters [%s] in port number "
171 "[%s], assuming default.\n", endptr, port_str));
172 port = 0;
173 }
174
175 if (port < 0 || port > 65535) {
176 PLUGIN_DEBUG(("Illegal port number [%d], assuming default.\n",
177 port));
178 port = 0;
179 }
180 } else {
181 PLUGIN_DEBUG(("Illegal port number [%s], assuming default.\n",
182 port_str));
183 port = 0;
184 }
185 }
186
187 switch (svc) {
188 case locate_service_kdc:
189 free(ctx->kdc_addr);
190 ctx->kdc_addr = strdup((char *) buf);
191 if (ctx->kdc_addr == NULL) {
192 PLUGIN_DEBUG(("strdup failed.\n"));
193 ret = ENOMEM;
194 goto done;
195 }
196 ctx->kdc_port = (uint16_t) port;
197 break;
198 case locate_service_kpasswd:
199 free(ctx->kpasswd_addr);
200 ctx->kpasswd_addr = strdup((char *) buf);
201 if (ctx->kpasswd_addr == NULL) {
202 PLUGIN_DEBUG(("strdup failed.\n"));
203 ret = ENOMEM;
204 goto done;
205 }
206 ctx->kpasswd_port = (uint16_t) port;
207 break;
208 default:
209 PLUGIN_DEBUG(("Unsupported service [%d].\n", svc));
210 ret = EINVAL;
211 goto done;
212 }
213
214 done:
215 free(krb5info_name);
216 return ret;
217 }
218
219 krb5_error_code sssd_krb5_locator_init(krb5_context context,
220 void **private_data)
221 {
222 struct sssd_ctx *ctx;
223 const char *dummy;
224
225 ctx = calloc(1,sizeof(struct sssd_ctx));
226 if (ctx == NULL) return KRB5_PLUGIN_NO_HANDLE;
227
228 dummy = getenv(SSSD_KRB5_LOCATOR_DEBUG);
229 if (dummy == NULL) {
230 ctx->debug = false;
231 } else {
232 ctx->debug = true;
233 PLUGIN_DEBUG(("sssd_krb5_locator_init called\n"));
234 }
235
236 *private_data = ctx;
237
238 return 0;
239 }
240
241 void sssd_krb5_locator_close(void *private_data)
242 {
243 struct sssd_ctx *ctx;
244
245 if (private_data == NULL) return;
246
247 ctx = (struct sssd_ctx *) private_data;
248 PLUGIN_DEBUG(("sssd_krb5_locator_close called\n"));
249
250 free(ctx->kdc_addr);
251 free(ctx->kpasswd_addr);
252 free(ctx->sssd_realm);
253 free(ctx);
254 private_data = NULL;
255
256 return;
257 }
258
259 krb5_error_code sssd_krb5_locator_lookup(void *private_data,
260 enum locate_service_type svc,
261 const char *realm,
262 int socktype,
263 int family,
264 int (*cbfunc)(void *, int, struct sockaddr *),
265 void *cbdata)
266 {
267 int ret;
268 struct addrinfo *ai;
269 struct sssd_ctx *ctx;
270 struct addrinfo ai_hints;
271 uint16_t port = 0;
272 const char *addr = NULL;
273 char port_str[PORT_STR_SIZE];
274
275 if (private_data == NULL) return KRB5_PLUGIN_NO_HANDLE;
276 ctx = (struct sssd_ctx *) private_data;
277
278 if (ctx->sssd_realm == NULL || strcmp(ctx->sssd_realm, realm) != 0) {
279 free(ctx->sssd_realm);
280 ctx->sssd_realm = strdup(realm);
281 if (ctx->sssd_realm == NULL) {
282 PLUGIN_DEBUG(("strdup failed.\n"));
283 return KRB5_PLUGIN_NO_HANDLE;
284 }
285
286 ret = get_krb5info(realm, ctx, locate_service_kdc);
287 if (ret != EOK) {
288 PLUGIN_DEBUG(("get_krb5info failed.\n"));
289 return KRB5_PLUGIN_NO_HANDLE;
290 }
291
292 if (svc == locate_service_kadmin || svc == locate_service_kpasswd ||
293 svc == locate_service_master_kdc) {
294 ret = get_krb5info(realm, ctx, locate_service_kpasswd);
295 if (ret != EOK) {
296 PLUGIN_DEBUG(("reading kpasswd address failed, "
297 "using kdc address.\n"));
298 free(ctx->kpasswd_addr);
299 ctx->kpasswd_addr = strdup(ctx->kdc_addr);
300 ctx->kpasswd_port = 0;
301 }
302 }
303 }
304
305 PLUGIN_DEBUG(("sssd_realm[%s] requested realm[%s] family[%d] socktype[%d] "
306 "locate_service[%d]\n", ctx->sssd_realm, realm, family,
307 socktype, svc));
308
309 switch (svc) {
310 case locate_service_kdc:
311 addr = ctx->kdc_addr;
312 port = ctx->kdc_port ? ctx->kdc_port : DEFAULT_KERBEROS_PORT;
313 break;
314 case locate_service_master_kdc:
315 addr = ctx->kpasswd_addr;
316 port = DEFAULT_KERBEROS_PORT;
317 break;
318 case locate_service_kadmin:
319 addr = ctx->kpasswd_addr;
320 port = DEFAULT_KADMIN_PORT;
321 break;
322 case locate_service_kpasswd:
323 addr = ctx->kpasswd_addr;
324 port = ctx->kpasswd_port ? ctx->kpasswd_port : DEFAULT_KPASSWD_PORT;
325 break;
326 case locate_service_krb524:
327 return KRB5_PLUGIN_NO_HANDLE;
328 default:
329 return KRB5_PLUGIN_NO_HANDLE;
330 }
331
332 switch (family) {
333 case AF_UNSPEC:
334 case AF_INET:
335 case AF_INET6:
336 break;
337 default:
338 return KRB5_PLUGIN_NO_HANDLE;
339 }
340
341 switch (socktype) {
342 case SOCK_STREAM:
343 case SOCK_DGRAM:
344 break;
345 default:
346 return KRB5_PLUGIN_NO_HANDLE;
347 }
348
349 if (strcmp(realm, ctx->sssd_realm) != 0)
350 return KRB5_PLUGIN_NO_HANDLE;
351
352 memset(port_str, 0, PORT_STR_SIZE);
353 ret = snprintf(port_str, PORT_STR_SIZE-1, "%u", port);
354 if (ret < 0 || ret >= (PORT_STR_SIZE-1)) {
355 PLUGIN_DEBUG(("snprintf failed.\n"));
356 return KRB5_PLUGIN_NO_HANDLE;
357 }
358
359 memset(&ai_hints, 0, sizeof(struct addrinfo));
360 ai_hints.ai_flags = AI_NUMERICHOST|AI_NUMERICSERV;
361 ai_hints.ai_socktype = socktype;
362
363 ret = getaddrinfo(addr, port_str, &ai_hints, &ai);
364 if (ret != 0) {
365 PLUGIN_DEBUG(("getaddrinfo failed [%d][%s].\n", ret,
366 gai_strerror(ret)));
367 if (ret == EAI_SYSTEM) {
368 PLUGIN_DEBUG(("getaddrinfo failed [%d][%s].\n", errno,
369 strerror(errno)));
370 }
371 return KRB5_PLUGIN_NO_HANDLE;
372 }
373
374 PLUGIN_DEBUG(("addr[%s:%s] family[%d] socktype[%d]\n", addr, port_str,
375 ai->ai_family, ai->ai_socktype));
376
377 if ((family == AF_UNSPEC || ai->ai_family == family) &&
378 ai->ai_socktype == socktype) {
379
380 ret = cbfunc(cbdata, socktype, ai->ai_addr);
381 if (ret != 0) {
382 PLUGIN_DEBUG(("cbfunc failed\n"));
383 return ret;
384 } else {
385 PLUGIN_DEBUG(("[%s] used\n", addr));
386 }
387 } else {
388 PLUGIN_DEBUG(("[%s] NOT used\n", addr));
389 }
390
391 return 0;
392 }
393
394 const krb5plugin_service_locate_ftable service_locator = {
395 0, /* version */
396 sssd_krb5_locator_init,
397 sssd_krb5_locator_close,
398 sssd_krb5_locator_lookup,
399 };