1/* 2 Unix SMB/CIFS implementation. 3 kerberos locator plugin 4 Copyright (C) Guenther Deschner 2007-2008 5 6 This program is free software; you can redistribute it and/or modify 7 it under the terms of the GNU General Public License as published by 8 the Free Software Foundation; either version 3 of the License, or 9 (at your option) any later version. 10 11 This program is distributed in the hope that it will be useful, 12 but WITHOUT ANY WARRANTY; without even the implied warranty of 13 MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the 14 GNU General Public License for more details. 15 16 You should have received a copy of the GNU General Public License 17 along with this program. If not, see <http://www.gnu.org/licenses/>. 18*/ 19 20#include "nsswitch/winbind_client.h" 21#include "libwbclient/wbclient.h" 22 23#ifndef DEBUG_KRB5 24#undef DEBUG_KRB5 25#endif 26 27#if defined(HAVE_KRB5) && defined(HAVE_KRB5_LOCATE_PLUGIN_H) 28 29#if HAVE_COM_ERR_H 30#include <com_err.h> 31#endif 32 33#include <krb5.h> 34#include <krb5/locate_plugin.h> 35 36#ifndef KRB5_PLUGIN_NO_HANDLE 37#define KRB5_PLUGIN_NO_HANDLE KRB5_KDC_UNREACH /* Heimdal */ 38#endif 39 40static const char *get_service_from_locate_service_type(enum locate_service_type svc) 41{ 42 switch (svc) { 43 case locate_service_kdc: 44 case locate_service_master_kdc: 45 return "88"; 46 case locate_service_kadmin: 47 case locate_service_krb524: 48 /* not supported */ 49 return NULL; 50 case locate_service_kpasswd: 51 return "464"; 52 default: 53 break; 54 } 55 return NULL; 56 57} 58 59#ifdef DEBUG_KRB5 60static const char *locate_service_type_name(enum locate_service_type svc) 61{ 62 switch (svc) { 63 case locate_service_kdc: 64 return "locate_service_kdc"; 65 case locate_service_master_kdc: 66 return "locate_service_master_kdc"; 67 case locate_service_kadmin: 68 return "locate_service_kadmin"; 69 case locate_service_krb524: 70 return "locate_service_krb524"; 71 case locate_service_kpasswd: 72 return "locate_service_kpasswd"; 73 default: 74 break; 75 } 76 return NULL; 77} 78 79static const char *socktype_name(int socktype) 80{ 81 switch (socktype) { 82 case SOCK_STREAM: 83 return "SOCK_STREAM"; 84 case SOCK_DGRAM: 85 return "SOCK_DGRAM"; 86 default: 87 break; 88 } 89 return "unknown"; 90} 91 92static const char *family_name(int family) 93{ 94 switch (family) { 95 case AF_UNSPEC: 96 return "AF_UNSPEC"; 97 case AF_INET: 98 return "AF_INET"; 99#if defined(HAVE_IPV6) 100 case AF_INET6: 101 return "AF_INET6"; 102#endif 103 default: 104 break; 105 } 106 return "unknown"; 107} 108#endif 109 110/** 111 * Check input parameters, return KRB5_PLUGIN_NO_HANDLE for unsupported ones 112 * 113 * @param svc 114 * @param realm string 115 * @param socktype integer 116 * @param family integer 117 * 118 * @return integer. 119 */ 120 121static int smb_krb5_locator_lookup_sanity_check(enum locate_service_type svc, 122 const char *realm, 123 int socktype, 124 int family) 125{ 126 if (!realm || strlen(realm) == 0) { 127 return EINVAL; 128 } 129 130 switch (svc) { 131 case locate_service_kdc: 132 case locate_service_master_kdc: 133 case locate_service_kpasswd: 134 break; 135 case locate_service_kadmin: 136 case locate_service_krb524: 137 return KRB5_PLUGIN_NO_HANDLE; 138 default: 139 return EINVAL; 140 } 141 142 switch (family) { 143 case AF_UNSPEC: 144 case AF_INET: 145 break; 146#if defined(HAVE_IPV6) 147 case AF_INET6: 148 break; 149#endif 150 default: 151 return EINVAL; 152 } 153 154 switch (socktype) { 155 case SOCK_STREAM: 156 case SOCK_DGRAM: 157 case 0: /* Heimdal uses that */ 158 break; 159 default: 160 return EINVAL; 161 } 162 163 return 0; 164} 165 166/** 167 * Try to get addrinfo for a given host and call the krb5 callback 168 * 169 * @param name string 170 * @param service string 171 * @param in struct addrinfo hint 172 * @param cbfunc krb5 callback function 173 * @param cbdata void pointer cbdata 174 * 175 * @return krb5_error_code. 176 */ 177 178static krb5_error_code smb_krb5_locator_call_cbfunc(const char *name, 179 const char *service, 180 struct addrinfo *in, 181 int (*cbfunc)(void *, int, struct sockaddr *), 182 void *cbdata) 183{ 184 struct addrinfo *out = NULL; 185 int ret; 186 int count = 3; 187 188 while (count) { 189 190 ret = getaddrinfo(name, service, in, &out); 191 if (ret == 0) { 192 break; 193 } 194 195 if (ret == EAI_AGAIN) { 196 count--; 197 continue; 198 } 199 200#ifdef DEBUG_KRB5 201 fprintf(stderr, "[%5u]: smb_krb5_locator_lookup: " 202 "getaddrinfo failed: %s (%d)\n", 203 (unsigned int)getpid(), gai_strerror(ret), ret); 204#endif 205 206 return KRB5_PLUGIN_NO_HANDLE; 207 } 208 209 ret = cbfunc(cbdata, out->ai_socktype, out->ai_addr); 210#ifdef DEBUG_KRB5 211 if (ret) { 212 fprintf(stderr, "[%5u]: smb_krb5_locator_lookup: " 213 "failed to call callback: %s (%d)\n", 214 (unsigned int)getpid(), error_message(ret), ret); 215 } 216#endif 217 218 freeaddrinfo(out); 219 return ret; 220} 221 222/** 223 * PUBLIC INTERFACE: locate init 224 * 225 * @param context krb5_context 226 * @param privata_data pointer to private data pointer 227 * 228 * @return krb5_error_code. 229 */ 230 231static krb5_error_code smb_krb5_locator_init(krb5_context context, 232 void **private_data) 233{ 234 return 0; 235} 236 237/** 238 * PUBLIC INTERFACE: close locate 239 * 240 * @param private_data pointer to private data 241 * 242 * @return void. 243 */ 244 245static void smb_krb5_locator_close(void *private_data) 246{ 247 return; 248} 249 250 251static bool ask_winbind(const char *realm, char **dcname) 252{ 253 wbcErr wbc_status; 254 const char *dc = NULL; 255 struct wbcDomainControllerInfoEx *dc_info = NULL; 256 uint32_t flags; 257 258 flags = WBC_LOOKUP_DC_KDC_REQUIRED | 259 WBC_LOOKUP_DC_IS_DNS_NAME | 260 WBC_LOOKUP_DC_RETURN_DNS_NAME | 261 WBC_LOOKUP_DC_IP_REQUIRED; 262 263 wbc_status = wbcLookupDomainControllerEx(realm, NULL, NULL, flags, &dc_info); 264 265 if (!WBC_ERROR_IS_OK(wbc_status)) { 266#ifdef DEBUG_KRB5 267 fprintf(stderr,"[%5u]: smb_krb5_locator_lookup: failed with: %s\n", 268 (unsigned int)getpid(), wbcErrorString(wbc_status)); 269#endif 270 return false; 271 } 272 273 if (dc_info->dc_address) { 274 dc = dc_info->dc_address; 275 if (dc[0] == '\\') dc++; 276 if (dc[0] == '\\') dc++; 277 } 278 279 if (!dc && dc_info->dc_unc) { 280 dc = dc_info->dc_unc; 281 if (dc[0] == '\\') dc++; 282 if (dc[0] == '\\') dc++; 283 } 284 285 if (!dc) { 286 wbcFreeMemory(dc_info); 287 return false; 288 } 289 290 *dcname = strdup(dc); 291 if (!*dcname) { 292 wbcFreeMemory(dc_info); 293 return false; 294 } 295 296 wbcFreeMemory(dc_info); 297 return true; 298} 299 300/** 301 * PUBLIC INTERFACE: locate lookup 302 * 303 * @param private_data pointer to private data 304 * @param svc enum locate_service_type. 305 * @param realm string 306 * @param socktype integer 307 * @param family integer 308 * @param cbfunc callback function to send back entries 309 * @param cbdata void pointer to cbdata 310 * 311 * @return krb5_error_code. 312 */ 313 314static krb5_error_code smb_krb5_locator_lookup(void *private_data, 315 enum locate_service_type svc, 316 const char *realm, 317 int socktype, 318 int family, 319 int (*cbfunc)(void *, int, struct sockaddr *), 320 void *cbdata) 321{ 322 krb5_error_code ret; 323 struct addrinfo aihints; 324 char *kdc_name = NULL; 325 const char *service = get_service_from_locate_service_type(svc); 326 327 ZERO_STRUCT(aihints); 328 329#ifdef DEBUG_KRB5 330 fprintf(stderr,"[%5u]: smb_krb5_locator_lookup: called for '%s' " 331 "svc: '%s' (%d) " 332 "socktype: '%s' (%d), family: '%s' (%d)\n", 333 (unsigned int)getpid(), realm, 334 locate_service_type_name(svc), svc, 335 socktype_name(socktype), socktype, 336 family_name(family), family); 337#endif 338 ret = smb_krb5_locator_lookup_sanity_check(svc, realm, socktype, 339 family); 340 if (ret) { 341#ifdef DEBUG_KRB5 342 fprintf(stderr, "[%5u]: smb_krb5_locator_lookup: " 343 "returning ret: %s (%d)\n", 344 (unsigned int)getpid(), error_message(ret), ret); 345#endif 346 return ret; 347 } 348 349 if (!winbind_env_set()) { 350 if (!ask_winbind(realm, &kdc_name)) { 351#ifdef DEBUG_KRB5 352 fprintf(stderr, "[%5u]: smb_krb5_locator_lookup: " 353 "failed to query winbindd\n", 354 (unsigned int)getpid()); 355#endif 356 goto failed; 357 } 358 } else { 359 const char *env = NULL; 360 char *var = NULL; 361 if (asprintf(&var, "%s_%s", 362 WINBINDD_LOCATOR_KDC_ADDRESS, realm) == -1) { 363 goto failed; 364 } 365 env = getenv(var); 366 if (!env) { 367#ifdef DEBUG_KRB5 368 fprintf(stderr, "[%5u]: smb_krb5_locator_lookup: " 369 "failed to get kdc from env %s\n", 370 (unsigned int)getpid(), var); 371#endif 372 free(var); 373 goto failed; 374 } 375 free(var); 376 377 kdc_name = strdup(env); 378 if (!kdc_name) { 379 goto failed; 380 } 381 } 382#ifdef DEBUG_KRB5 383 fprintf(stderr, "[%5u]: smb_krb5_locator_lookup: " 384 "got '%s' for '%s' from winbindd\n", (unsigned int)getpid(), 385 kdc_name, realm); 386#endif 387 388 aihints.ai_family = family; 389 aihints.ai_socktype = socktype; 390 391 ret = smb_krb5_locator_call_cbfunc(kdc_name, 392 service, 393 &aihints, 394 cbfunc, cbdata); 395 SAFE_FREE(kdc_name); 396 397 return ret; 398 399 failed: 400 return KRB5_PLUGIN_NO_HANDLE; 401} 402 403#ifdef HEIMDAL_KRB5_LOCATE_PLUGIN_H 404#define SMB_KRB5_LOCATOR_SYMBOL_NAME resolve /* Heimdal */ 405#else 406#define SMB_KRB5_LOCATOR_SYMBOL_NAME service_locator /* MIT */ 407#endif 408 409const krb5plugin_service_locate_ftable SMB_KRB5_LOCATOR_SYMBOL_NAME = { 410 0, /* version */ 411 smb_krb5_locator_init, 412 smb_krb5_locator_close, 413 smb_krb5_locator_lookup, 414}; 415 416#endif 417