1 /*
2 * This file and its contents are supplied under the terms of the
3 * Common Development and Distribution License ("CDDL"), version 1.0.
4 * You may only use this file in accordance with the terms of version
5 * 1.0 of the CDDL.
6 *
7 * A full copy of the text of the CDDL should have accompanied this
8 * source. A copy of the CDDL is also available via the Internet at
9 * http://www.illumos.org/license/CDDL.
10 */
11
12 /*
13 * Copyright 2020 Nexenta by DDN, Inc. All rights reserved.
14 */
15
16 #include <stdio.h>
17 #include <stdlib.h>
18 #include <stdarg.h>
19 #include <string.h>
20 #include <syslog.h>
21
22 #include <sys/types.h>
23 #include <sys/errno.h>
24 #include <sys/socket.h>
25 #include <netinet/in.h>
26 #include <sys/note.h>
27
28 #include <smbsrv/libsmbns.h>
29 #include <ads/dsgetdc.h>
30
31 #include "smbd.h"
32 #include "locate_plugin.h"
33
34 /* osconf.h - sigh */
35 #define KRB5_DEFAULT_PORT 88
36 #define DEFAULT_KADM5_PORT 749
37 #define DEFAULT_KPASSWD_PORT 464
38
39 /*
40 * This is an "override plugin" used by libkrb5. See:
41 * lib/gss_mechs/mech_krb5/krb5/os/locate_kdc.c
42 *
43 * The interface is based on:
44 * http://web.mit.edu/~kerberos/krb5-1.12/doc/plugindev/locate.html
45 */
46
47 /*
48 * Called by krb5int_locate_server / override_locate_server
49 */
50
51 krb5_error_code
_krb5_override_service_locator(void * arg0,enum locate_service_type svc,const char * realm,int socktype,int family,int (* cbfunc)(void *,int,struct sockaddr *),void * cbdata)52 _krb5_override_service_locator(
53 void *arg0,
54 enum locate_service_type svc,
55 const char *realm,
56 int socktype,
57 int family,
58 int (*cbfunc)(void *, int, struct sockaddr *),
59 void *cbdata)
60 {
61 _NOTE(ARGUNUSED(arg0))
62 smb_domainex_t dxi;
63 int rc = KRB5_PLUGIN_NO_HANDLE;
64 short port;
65
66 /*
67 * Is this a service we want to override?
68 */
69 switch (svc) {
70 case locate_service_kdc:
71 case locate_service_master_kdc:
72 port = htons(KRB5_DEFAULT_PORT);
73 break;
74 case locate_service_kadmin:
75 port = htons(DEFAULT_KADM5_PORT);
76 break;
77 case locate_service_kpasswd:
78 port = htons(DEFAULT_KPASSWD_PORT);
79 break;
80 case locate_service_krb524:
81 default:
82 return (rc);
83 }
84
85 /*
86 * What's my domain? Note: have to get this in a way
87 * that works while join domain is underway.
88 */
89 if (!smb_domain_getinfo(&dxi)) {
90 smbd_report("_krb5_override_service_locator "
91 "failed getting domain info");
92 return (KRB5_ERR_HOST_REALM_UNKNOWN);
93 }
94
95 /*
96 * Is this a realm we want to override?
97 */
98 if (0 != strcasecmp(realm, dxi.d_primary.di_fqname)) {
99 syslog(LOG_DEBUG, "_krb5_override_service_locator, "
100 "realm=%s, fqdn=%s", realm, dxi.d_primary.di_fqname);
101 return (rc);
102 }
103
104 /*
105 * Yes, this is our domain. Have a DC?
106 */
107 if (dxi.d_dci.dc_name[0] == '\0' ||
108 dxi.d_dci.dc_addr.a_family == 0)
109 return (KRB5_REALM_CANT_RESOLVE);
110
111 if ((dxi.d_dci.dc_flags & DS_KDC_FLAG) == 0) {
112 smbd_report("_krb5_override_service_locator: "
113 "Domain Controller is not a KDC: "
114 "Kerberos auth may be slow");
115 return (rc);
116 }
117
118 switch (family) {
119 case AF_UNSPEC:
120 break; /* OK */
121 case AF_INET:
122 case AF_INET6:
123 if (family == dxi.d_dci.dc_addr.a_family)
124 break; /* OK */
125 /* else fallthrough */
126 default:
127 return (KRB5_ERR_NO_SERVICE);
128 }
129
130 /*
131 * Provide the service address we have.
132 */
133 switch (dxi.d_dci.dc_addr.a_family) {
134 case AF_INET: {
135 struct sockaddr_in sin;
136 (void) memset(&sin, 0, sizeof (sin));
137 sin.sin_family = AF_INET;
138 sin.sin_port = port;
139 (void) memcpy(&sin.sin_addr, &dxi.d_dci.dc_addr.a_ipv4,
140 sizeof (sin.sin_addr));
141 rc = cbfunc(cbdata, socktype, (struct sockaddr *)&sin);
142 /* rc from cbfunc is special. */
143 if (rc)
144 rc = ENOMEM;
145 break;
146 }
147 case AF_INET6: {
148 struct sockaddr_in6 sin6;
149 (void) memset(&sin6, 0, sizeof (sin6));
150 sin6.sin6_family = AF_INET6;
151 sin6.sin6_port = port;
152 (void) memcpy(&sin6.sin6_addr, &dxi.d_dci.dc_addr.a_ipv6,
153 sizeof (sin6.sin6_addr));
154 rc = cbfunc(cbdata, socktype, (struct sockaddr *)&sin6);
155 /* rc from cbfunc is special. */
156 if (rc)
157 rc = ENOMEM;
158 break;
159 }
160 default:
161 rc = KRB5_ERR_NO_SERVICE;
162 break;
163 }
164
165 return (rc);
166 }
167