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