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;
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	default:
128	    ret = KRB5_PLUGIN_NO_HANDLE;
129	    goto out;
130	}
131
132	store = SCDynamicStoreCreate(kCFAllocatorDefault, CFSTR("Kerberos"), NULL, NULL);
133	sckey = [NSString stringWithFormat:@"%@%s",
134			  (NSString *)HEIMDAL_SC_LOCATE_REALM_PREFIX, realm];
135	top = (NSDictionary *)SCDynamicStoreCopyValue(store, (CFStringRef)sckey);
136	if (top == NULL) {
137	    ret = KRB5_PLUGIN_NO_HANDLE;
138	    goto out;
139	}
140
141	vals = [top valueForKey:svc];
142	if (vals == NULL) {
143	    ret = KRB5_PLUGIN_NO_HANDLE;
144	    goto out;
145	}
146
147	if ([vals count] == 0)
148	    syslog(LOG_WARNING,
149		   "Kerberos-Reachability SystemConfiguration returned 0 entries for %s",
150		   realm);
151
152	for (NSDictionary *a in vals) {
153	    host = [a valueForKey:(NSString *)HEIMDAL_SC_LOCATE_HOST];
154
155	    rp = [a valueForKey:(NSString *)HEIMDAL_SC_LOCATE_PORT];
156	    if ([rp isKindOfClass:[NSString class]])
157		port = rp;
158	    else if ([rp respondsToSelector:@selector(stringValue)])
159		port = [rp stringValue];
160	    else
161		port = defport;
162	    if (port == nil)
163		continue;
164
165	    memset(&hints, 0, sizeof(hints));
166	    hints.ai_flags = 0;
167	    hints.ai_family = type;
168	    hints.ai_socktype = domain;
169
170	    if (getaddrinfo([host UTF8String], [port UTF8String], &hints, &ai0) != 0)
171		continue;
172
173	    for (ai = ai0; ai != NULL; ai = ai->ai_next) {
174		ret = addfunc(addctx, ai->ai_socktype, ai->ai_addr);
175		if (ret == 0)
176		    found_entry = 1;
177	    }
178	    freeaddrinfo(ai0);
179	}
180
181	if (!found_entry)
182	    ret = KRB5_KDC_UNREACH;
183	else
184	    ret = 0;
185     out:
186	do {} while(0);
187    }
188    @catch (NSException *exception) { }
189    @finally {
190
191	if (top)
192	    CFRelease((CFTypeRef)top);
193	if (store)
194	    CFRelease(store);
195	[pool drain];
196    }
197
198    return ret;
199}
200
201static krb5_error_code
202reachability_lookup_old(void *ctx,
203			enum locate_service_type service,
204			const char *realm,
205			int domain,
206			int type,
207			int (*addfunc)(void *,int,struct sockaddr *),
208			void *addctx)
209{
210    return reachability_lookup(ctx, KRB5_PLF_ALLOW_HOMEDIR, service,
211			       realm, domain, type, addfunc, addctx);
212}
213
214krb5plugin_service_locate_ftable service_locator = {
215    KRB5_PLUGIN_LOCATE_VERSION_2,
216    reachability_init,
217    reachability_fini,
218    reachability_lookup_old,
219    reachability_lookup
220};
221