1/*
2 *  LKDC-lookup-plugin.c
3 *  LocalKDC
4 */
5
6/*
7 * Copyright (c) 2006-2007 Apple Inc. All rights reserved.
8 *
9 * @APPLE_LICENSE_HEADER_START@
10 *
11 * This file contains Original Code and/or Modifications of Original Code
12 * as defined in and that are subject to the Apple Public Source License
13 * Version 2.0 (the 'License'). You may not use this file except in
14 * compliance with the License. Please obtain a copy of the License at
15 * http://www.opensource.apple.com/apsl/ and read it before using this
16 * file.
17 *
18 * The Original Code and all software distributed under the License are
19 * distributed on an 'AS IS' basis, WITHOUT WARRANTY OF ANY KIND, EITHER
20 * EXPRESS OR IMPLIED, AND APPLE HEREBY DISCLAIMS ALL SUCH WARRANTIES,
21 * INCLUDING WITHOUT LIMITATION, ANY WARRANTIES OF MERCHANTABILITY,
22 * FITNESS FOR A PARTICULAR PURPOSE, QUIET ENJOYMENT OR NON-INFRINGEMENT.
23 * Please see the License for the specific language governing rights and
24 * limitations under the License.
25 *
26 * @APPLE_LICENSE_HEADER_END@
27 */
28
29#include "LKDC-lookup-plugin.h"
30#include <asl.h>
31#include <errno.h>
32#include <stdarg.h>
33#include <stdlib.h>
34#include <string.h>
35#include <stdio.h>
36#include <arpa/inet.h>
37#include <netdb.h>
38
39#include <Heimdal/krb5.h>
40#include <Heimdal/locate_plugin.h>
41
42#include "LKDCHelper.h"
43
44typedef struct _state {
45	krb5_context krb_context;
46	time_t	cacheCreateTime;
47} state;
48
49#define LKDC_PLUGIN_DEBUG 1
50#if LKDC_PLUGIN_DEBUG
51#define debug(fmt, ...) do { debug_(__func__, fmt, ## __VA_ARGS__); } while(0)
52static inline void
53debug_(const char *func, const char *fmt, ...)
54{
55	static const char ellipsis[] = "[...]";
56	char buf[2048];
57	char *p = buf;
58	char *endp = &buf[sizeof(buf)];
59	ssize_t n = snprintf(p, endp-p, "%s: ", func);
60
61	if ((size_t)n >= endp-p)
62		return;
63	p += n;
64	va_list ap;
65	va_start(ap, fmt);
66	n = vsnprintf(p, endp-p, fmt, ap);
67	va_end(ap);
68	if ((size_t)n >= endp-p)
69		snprintf(endp-sizeof(ellipsis), sizeof(ellipsis), ellipsis);
70	asl_log(NULL, NULL, ASL_LEVEL_DEBUG, "%s", buf);
71}
72#else
73#define debug(...)
74#endif
75
76static krb5_error_code LKDCInit (krb5_context c, void **ptr)
77{
78	return 0;
79}
80
81static void LKDCFinish (void *ptr) {
82	return;
83}
84
85static krb5_error_code LKDCLookup (void *ptr,
86								   enum locate_service_type svc,
87								   const char *realm,
88								   int socktype,
89								   int family,
90								   int (*cbfunc)(void *, int, struct sockaddr *),
91								   void *cbdata)
92{
93	krb5_error_code error = KRB5_PLUGIN_NO_HANDLE;
94	char			*hostname = NULL;
95	struct addrinfo  aiHints;
96	struct addrinfo *aiResult = NULL;
97	struct addrinfo *res = NULL;
98	uint16_t		port;
99	int				err = 0;
100
101	switch (family) {
102	case 0:
103	case AF_INET:
104	case AF_INET6:
105		break;
106	default:
107		debug("Declined to handle address family %d", family);
108		return error;
109	}
110
111	debug("svc = %d, realm = %s, family= %d, socktype = %d", svc, realm, family, socktype);
112	switch (svc) {
113		case locate_service_kdc:
114		case locate_service_master_kdc:
115			break;
116		case locate_service_kadmin:
117		case locate_service_krb524:
118		case locate_service_kpasswd:
119		default:
120			return error;
121	}
122	debug("KDC|MasterKDC");
123
124	/* Do we handle this type of realm? */
125	if (strncmp ("LKDC:", realm, 5) != 0) {
126		return error;
127	}
128
129	err = LKDCFindKDCForRealm (realm, &hostname, &port);
130	if (0 != err || NULL == hostname) {
131		return error;
132	}
133
134	bzero (&aiHints, sizeof (aiHints));
135	aiHints.ai_flags = 0;
136	aiHints.ai_family = family;
137	aiHints.ai_socktype = socktype;
138
139	err = getaddrinfo (hostname,
140					   NULL,	// realmDetails->servicePortName?
141					   &aiHints,
142					   &aiResult);
143	debug("getaddrinfo () == %d", err);
144
145	if (0 == err) {
146		for (res = aiResult; res != NULL; res = res->ai_next) {
147			void *in_addr = NULL;
148			uint16_t in_port = 0;
149
150			debug("0x%08p: family = %d, socktype = %d, protocol = %d", res, res->ai_family, res->ai_socktype, res->ai_protocol);
151			debug("Running callback 0x%08p", res);
152			switch (res->ai_family) {
153			case AF_INET:
154				in_addr = &(((struct sockaddr_in *)res->ai_addr)->sin_addr);
155				((struct sockaddr_in *)res->ai_addr)->sin_port = htons (in_port = port);
156				break;
157			case AF_INET6:
158				in_addr = &(((struct sockaddr_in6 *)res->ai_addr)->sin6_addr);
159				((struct sockaddr_in6 *)res->ai_addr)->sin6_port = htons (in_port = port);
160				break;
161			default:
162				in_addr = NULL;
163				debug("Unexpected address family %d", res->ai_family);
164				break;
165			}
166			if (NULL != in_addr) {
167				err = cbfunc (cbdata, res->ai_socktype, res->ai_addr);
168				debug("Callback done 0x%08p, err=%d", res, err);
169#if LKDC_PLUGIN_DEBUG
170				{
171					char ipString[1024];
172					ipString[0] = '\0';
173					if (NULL == inet_ntop(res->ai_family, in_addr, ipString, sizeof(ipString)))
174						debug("inet_ntop failed: %s", strerror(errno));
175					else
176						debug("addr = %s, port = %d", ipString, (int)in_port);
177				}
178#endif
179			}
180
181		}
182		freeaddrinfo (aiResult);
183		aiResult = NULL;
184	} else {
185		debug("failed %d", error);
186		return error;
187	}
188
189	debug("OK");
190	return 0;
191}
192
193/* Structure used by Kerberos to locate the functions to call for this plugin. */
194
195const krb5plugin_service_locate_ftable service_locator = {
196    0,	/* version */
197	LKDCInit,		/* Initialize - called each time */
198	LKDCFinish,		/* Finish - called each time */
199	LKDCLookup,		/* Lookup function */
200};
201