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  	};