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