1/*
2 * Copyright (c) 2006 Kungliga Tekniska Högskolan
3 * (Royal Institute of Technology, Stockholm, Sweden).
4 * All rights reserved.
5 *
6 * Portions Copyright (c) 2010 Apple Inc. All rights reserved.
7 *
8 * Redistribution and use in source and binary forms, with or without
9 * modification, are permitted provided that the following conditions
10 * are met:
11 *
12 * 1. Redistributions of source code must retain the above copyright
13 *    notice, this list of conditions and the following disclaimer.
14 *
15 * 2. Redistributions in binary form must reproduce the above copyright
16 *    notice, this list of conditions and the following disclaimer in the
17 *    documentation and/or other materials provided with the distribution.
18 *
19 * 3. Neither the name of the Institute nor the names of its contributors
20 *    may be used to endorse or promote products derived from this software
21 *    without specific prior written permission.
22 *
23 * THIS SOFTWARE IS PROVIDED BY THE INSTITUTE AND CONTRIBUTORS ``AS IS'' AND
24 * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
25 * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
26 * ARE DISCLAIMED.  IN NO EVENT SHALL THE INSTITUTE OR CONTRIBUTORS BE LIABLE
27 * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
28 * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS
29 * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION)
30 * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT
31 * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY
32 * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF
33 * SUCH DAMAGE.
34 */
35
36#import <err.h>
37#import <stdio.h>
38#import <arpa/inet.h>
39#import <netdb.h>
40#import <sys/param.h>
41#import <sys/socket.h>
42#import <syslog.h>
43
44#import <Heimdal/HeimdalSystemConfiguration.h>
45
46#import <Foundation/Foundation.h>
47#import <SystemConfiguration/SystemConfiguration.h>
48#import <krb5.h>
49#import <locate_plugin.h>
50
51/**
52 * Reachablity plugin reads System Configuration to pick up the realm
53 * configuration from OpenDirectory plugins, both OD and AD.
54 *
55 * The keys published is:
56 *
57 * Kerberos:REALM = {
58 *   kadmin = [ { host = "hostname", port = "port-number" } ]
59 *   kdc = [ .. ]
60 *   kpasswd = [ ]
61 * }
62 *
63 * port is optional
64 *
65 * The following behaivor is expected:
66 *
67 * 1. Not joined to a domain
68 *      no entry published
69 * 2. Joined to a domain and replica AVAILABLE:
70 *       entry pushlished with content
71 * 3. Joined to a domain and replica UNAVAILABLE
72 *       entry pushlished, but no content
73 *
74 */
75
76static krb5_error_code
77reachability_init(krb5_context context, void **ctx)
78{
79    *ctx = NULL;
80    return 0;
81}
82
83static void
84reachability_fini(void *ctx)
85{
86}
87
88static krb5_error_code
89reachability_lookup(void *ctx,
90		    unsigned long flags,
91		    enum locate_service_type service,
92		    const char *realm,
93		    int domain,
94		    int type,
95		    int (*addfunc)(void *,int,struct sockaddr *),
96		    void *addctx)
97{
98    krb5_error_code ret;
99    NSAutoreleasePool *pool;
100    NSString *svc, *sckey, *host, *port;
101    struct addrinfo hints, *ai0, *ai;
102    SCDynamicStoreRef store = NULL;
103    NSDictionary *top = NULL;
104    NSArray *vals;
105    NSString *defport = NULL;
106    int found_entry = 0;
107    id rp;
108
109    @try {
110	pool = [[NSAutoreleasePool alloc] init];
111
112	switch(service) {
113	case locate_service_kdc:
114	case locate_service_master_kdc:
115	case locate_service_krb524:
116	    svc = (NSString *)HEIMDAL_SC_LOCATE_TYPE_KDC;
117	    defport = @"88";
118	    break;
119	case locate_service_kpasswd:
120	    svc = (NSString *)HEIMDAL_SC_LOCATE_TYPE_KPASSWD;
121	    defport = @"464";
122	    break;
123	case locate_service_kadmin:
124	    svc = (NSString *)HEIMDAL_SC_LOCATE_TYPE_ADMIN;
125	    defport = @"749";
126	    break;
127	}
128	if (defport == NULL) {
129	    ret = KRB5_PLUGIN_NO_HANDLE;
130	    goto out;
131	}
132
133	store = SCDynamicStoreCreate(kCFAllocatorDefault, CFSTR("Kerberos"), NULL, NULL);
134	sckey = [NSString stringWithFormat:@"%@%s",
135			  (NSString *)HEIMDAL_SC_LOCATE_REALM_PREFIX, realm];
136	top = (NSDictionary *)SCDynamicStoreCopyValue(store, (CFStringRef)sckey);
137	if (top == NULL) {
138	    ret = KRB5_PLUGIN_NO_HANDLE;
139	    goto out;
140	}
141
142	vals = [top valueForKey:svc];
143	if (vals == NULL) {
144	    ret = KRB5_PLUGIN_NO_HANDLE;
145	    goto out;
146	}
147
148	if ([vals count] == 0)
149	    syslog(LOG_WARNING,
150		   "Kerberos-Reachability SystemConfiguration returned 0 entries for %s",
151		   realm);
152
153	for (NSDictionary *a in vals) {
154	    host = [a valueForKey:(NSString *)HEIMDAL_SC_LOCATE_HOST];
155
156	    rp = [a valueForKey:(NSString *)HEIMDAL_SC_LOCATE_PORT];
157	    if ([rp isKindOfClass:[NSString class]])
158		port = rp;
159	    else if ([rp respondsToSelector:@selector(stringValue)])
160		port = [rp stringValue];
161	    else
162		port = defport;
163	    if (port == nil)
164		continue;
165
166	    memset(&hints, 0, sizeof(hints));
167	    hints.ai_flags = 0;
168	    hints.ai_family = type;
169	    hints.ai_socktype = domain;
170
171	    if (getaddrinfo([host UTF8String], [port UTF8String], &hints, &ai0) != 0)
172		continue;
173
174	    for (ai = ai0; ai != NULL; ai = ai->ai_next) {
175		ret = addfunc(addctx, ai->ai_socktype, ai->ai_addr);
176		if (ret == 0)
177		    found_entry = 1;
178	    }
179	    freeaddrinfo(ai0);
180	}
181
182	if (!found_entry)
183	    ret = KRB5_KDC_UNREACH;
184	else
185	    ret = 0;
186     out:
187	do {} while(0);
188    }
189    @catch (NSException * __unused exception) { }
190    @finally {
191
192	if (top)
193	    CFRelease((CFTypeRef)top);
194	if (store)
195	    CFRelease(store);
196	[pool drain];
197    }
198
199    return ret;
200}
201
202static krb5_error_code
203reachability_lookup_old(void *ctx,
204			enum locate_service_type service,
205			const char *realm,
206			int domain,
207			int type,
208			int (*addfunc)(void *,int,struct sockaddr *),
209			void *addctx)
210{
211    return reachability_lookup(ctx, KRB5_PLF_ALLOW_HOMEDIR, service,
212			       realm, domain, type, addfunc, addctx);
213}
214
215krb5plugin_service_locate_ftable service_locator = {
216    KRB5_PLUGIN_LOCATE_VERSION_2,
217    reachability_init,
218    reachability_fini,
219    reachability_lookup_old,
220    reachability_lookup
221};
222