1/* 2 Unix SMB/CIFS implementation. 3 kerberos locator plugin 4 Copyright (C) Guenther Deschner 2007 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 2 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, write to the Free Software 18 Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA. 19*/ 20 21#include "includes.h" 22 23#if defined(HAVE_KRB5) && defined(HAVE_KRB5_LOCATE_PLUGIN_H) 24 25#include <krb5/locate_plugin.h> 26 27static const char *get_service_from_locate_service_type(enum locate_service_type svc) 28{ 29 switch (svc) { 30 case locate_service_kdc: 31 case locate_service_master_kdc: 32 return "88"; 33 case locate_service_kadmin: 34 case locate_service_krb524: 35 /* not supported */ 36 return NULL; 37 case locate_service_kpasswd: 38 return "464"; 39 default: 40 break; 41 } 42 return NULL; 43 44} 45 46static const char *locate_service_type_name(enum locate_service_type svc) 47{ 48 switch (svc) { 49 case locate_service_kdc: 50 return "locate_service_kdc"; 51 case locate_service_master_kdc: 52 return "locate_service_master_kdc"; 53 case locate_service_kadmin: 54 return "locate_service_kadmin"; 55 case locate_service_krb524: 56 return "locate_service_krb524"; 57 case locate_service_kpasswd: 58 return "locate_service_kpasswd"; 59 default: 60 break; 61 } 62 return NULL; 63} 64 65static const char *socktype_name(int socktype) 66{ 67 switch (socktype) { 68 case SOCK_STREAM: 69 return "SOCK_STREAM"; 70 case SOCK_DGRAM: 71 return "SOCK_DGRAM"; 72 default: 73 break; 74 } 75 return "unknown"; 76} 77 78static const char *family_name(int family) 79{ 80 switch (family) { 81 case AF_UNSPEC: 82 return "AF_UNSPEC"; 83 case AF_INET: 84 return "AF_INET"; 85 case AF_INET6: 86 return "AF_INET6"; 87 default: 88 break; 89 } 90 return "unknown"; 91} 92 93/** 94 * Check input parameters, return KRB5_PLUGIN_NO_HANDLE for unsupported ones 95 * 96 * @param svc 97 * @param realm string 98 * @param socktype integer 99 * @param family integer 100 * 101 * @return integer. 102 */ 103 104static int smb_krb5_locator_lookup_sanity_check(enum locate_service_type svc, 105 const char *realm, 106 int socktype, 107 int family) 108{ 109 if (!realm || strlen(realm) == 0) { 110 return EINVAL; 111 } 112 113 switch (svc) { 114 case locate_service_kdc: 115 case locate_service_master_kdc: 116 case locate_service_kpasswd: 117 break; 118 case locate_service_kadmin: 119 case locate_service_krb524: 120#ifdef KRB5_PLUGIN_NO_HANDLE 121 return KRB5_PLUGIN_NO_HANDLE; 122#else 123 return KRB5_KDC_UNREACH; /* Heimdal */ 124#endif 125 default: 126 return EINVAL; 127 } 128 129 switch (family) { 130 case AF_UNSPEC: 131 case AF_INET: 132 break; 133 case AF_INET6: /* not yet */ 134#ifdef KRB5_PLUGIN_NO_HANDLE 135 return KRB5_PLUGIN_NO_HANDLE; 136#else 137 return KRB5_KDC_UNREACH; /* Heimdal */ 138#endif 139 default: 140 return EINVAL; 141 } 142 143 switch (socktype) { 144 case SOCK_STREAM: 145 case SOCK_DGRAM: 146 case 0: /* Heimdal uses that */ 147 break; 148 default: 149 return EINVAL; 150 } 151 152 return 0; 153} 154 155/** 156 * Try to get addrinfo for a given host and call the krb5 callback 157 * 158 * @param name string 159 * @param service string 160 * @param in struct addrinfo hint 161 * @param cbfunc krb5 callback function 162 * @param cbdata void pointer cbdata 163 * 164 * @return krb5_error_code. 165 */ 166 167static krb5_error_code smb_krb5_locator_call_cbfunc(const char *name, 168 const char *service, 169 struct addrinfo *in, 170 int (*cbfunc)(void *, int, struct sockaddr *), 171 void *cbdata) 172{ 173 struct addrinfo *out; 174 int ret; 175 int count = 3; 176 177 while (count) { 178 179 ret = getaddrinfo(name, service, in, &out); 180 if (ret == 0) { 181 break; 182 } 183 184 if (ret == EAI_AGAIN) { 185 count--; 186 continue; 187 } 188 189 DEBUG(10,("smb_krb5_locator_lookup: got ret: %s (%d)\n", 190 gai_strerror(ret), ret)); 191#ifdef KRB5_PLUGIN_NO_HANDLE 192 return KRB5_PLUGIN_NO_HANDLE; 193#else 194 return KRB5_KDC_UNREACH; /* Heimdal */ 195#endif 196 } 197 198 ret = cbfunc(cbdata, out->ai_socktype, out->ai_addr); 199 if (ret) { 200 DEBUG(10,("smb_krb5_locator_lookup: failed to call callback: %s (%d)\n", 201 error_message(ret), ret)); 202 } 203 204 freeaddrinfo(out); 205 206 return ret; 207} 208 209/** 210 * PUBLIC INTERFACE: locate init 211 * 212 * @param context krb5_context 213 * @param privata_data pointer to private data pointer 214 * 215 * @return krb5_error_code. 216 */ 217 218krb5_error_code smb_krb5_locator_init(krb5_context context, 219 void **private_data) 220{ 221 setup_logging("smb_krb5_locator", True); 222 load_case_tables(); 223 lp_load(dyn_CONFIGFILE,True,False,False,True); 224 225 DEBUG(10,("smb_krb5_locator_init: called\n")); 226 227 return 0; 228} 229 230/** 231 * PUBLIC INTERFACE: close locate 232 * 233 * @param private_data pointer to private data 234 * 235 * @return void. 236 */ 237 238void smb_krb5_locator_close(void *private_data) 239{ 240 DEBUG(10,("smb_krb5_locator_close: called\n")); 241 242 /* gfree_all(); */ 243} 244 245/** 246 * PUBLIC INTERFACE: locate lookup 247 * 248 * @param private_data pointer to private data 249 * @param svc enum locate_service_type. 250 * @param realm string 251 * @param socktype integer 252 * @param family integer 253 * @param cbfunc callback function to send back entries 254 * @param cbdata void pointer to cbdata 255 * 256 * @return krb5_error_code. 257 */ 258 259krb5_error_code smb_krb5_locator_lookup(void *private_data, 260 enum locate_service_type svc, 261 const char *realm, 262 int socktype, 263 int family, 264 int (*cbfunc)(void *, int, struct sockaddr *), 265 void *cbdata) 266{ 267 NTSTATUS status; 268 krb5_error_code ret; 269 char *sitename = NULL; 270 struct ip_service *ip_list; 271 int count = 0; 272 struct addrinfo aihints; 273 char *saf_name = NULL; 274 int i; 275 276 DEBUG(10,("smb_krb5_locator_lookup: called for\n")); 277 DEBUGADD(10,("\tsvc: %s (%d), realm: %s\n", 278 locate_service_type_name(svc), svc, realm)); 279 DEBUGADD(10,("\tsocktype: %s (%d), family: %s (%d)\n", 280 socktype_name(socktype), socktype, 281 family_name(family), family)); 282 283 ret = smb_krb5_locator_lookup_sanity_check(svc, realm, socktype, family); 284 if (ret) { 285 DEBUG(10,("smb_krb5_locator_lookup: returning ret: %s (%d)\n", 286 error_message(ret), ret)); 287 return ret; 288 } 289 290 /* first try to fetch from SAF cache */ 291 292 saf_name = saf_fetch(realm); 293 if (!saf_name || strlen(saf_name) == 0) { 294 DEBUG(10,("smb_krb5_locator_lookup: no SAF name stored for %s\n", 295 realm)); 296 goto find_kdc; 297 } 298 299 DEBUG(10,("smb_krb5_locator_lookup: got %s for %s from SAF cache\n", 300 saf_name, realm)); 301 302 ZERO_STRUCT(aihints); 303 304 aihints.ai_family = family; 305 aihints.ai_socktype = socktype; 306 307 ret = smb_krb5_locator_call_cbfunc(saf_name, 308 get_service_from_locate_service_type(svc), 309 &aihints, 310 cbfunc, cbdata); 311 if (ret) { 312 return ret; 313 } 314 315 return 0; 316 317 find_kdc: 318 319 /* now try to find via site-aware DNS SRV query */ 320 321 sitename = sitename_fetch(realm); 322 status = get_kdc_list(realm, sitename, &ip_list, &count); 323 324 /* if we didn't found any KDCs on our site go to the main list */ 325 326 if (NT_STATUS_IS_OK(status) && sitename && (count == 0)) { 327 SAFE_FREE(ip_list); 328 SAFE_FREE(sitename); 329 status = get_kdc_list(realm, NULL, &ip_list, &count); 330 } 331 332 SAFE_FREE(sitename); 333 334 if (!NT_STATUS_IS_OK(status)) { 335 DEBUG(10,("smb_krb5_locator_lookup: got %s (%s)\n", 336 nt_errstr(status), 337 error_message(nt_status_to_krb5(status)))); 338#ifdef KRB5_PLUGIN_NO_HANDLE 339 return KRB5_PLUGIN_NO_HANDLE; 340#else 341 return KRB5_KDC_UNREACH; /* Heimdal */ 342#endif 343 } 344 345 for (i=0; i<count; i++) { 346 347 const char *host = NULL; 348 const char *port = NULL; 349 350 ZERO_STRUCT(aihints); 351 352 aihints.ai_family = family; 353 aihints.ai_socktype = socktype; 354 355 host = inet_ntoa(ip_list[i].ip); 356 port = get_service_from_locate_service_type(svc); 357 358 ret = smb_krb5_locator_call_cbfunc(host, 359 port, 360 &aihints, 361 cbfunc, cbdata); 362 if (ret) { 363 /* got error */ 364 break; 365 } 366 } 367 368 SAFE_FREE(ip_list); 369 370 return ret; 371} 372 373#ifdef HEIMDAL_KRB5_LOCATE_PLUGIN_H 374#define SMB_KRB5_LOCATOR_SYMBOL_NAME resolve /* Heimdal */ 375#else 376#define SMB_KRB5_LOCATOR_SYMBOL_NAME service_locator /* MIT */ 377#endif 378 379const krb5plugin_service_locate_ftable SMB_KRB5_LOCATOR_SYMBOL_NAME = { 380 0, /* version */ 381 smb_krb5_locator_init, 382 smb_krb5_locator_close, 383 smb_krb5_locator_lookup, 384}; 385 386#endif 387