root/nsswitch/winbind_krb5_locator.c

/* [<][>][^][v][top][bottom][index][help] */

DEFINITIONS

This source file includes following definitions.
  1. get_service_from_locate_service_type
  2. locate_service_type_name
  3. socktype_name
  4. family_name
  5. smb_krb5_locator_lookup_sanity_check
  6. smb_krb5_locator_call_cbfunc
  7. smb_krb5_locator_init
  8. smb_krb5_locator_close
  9. ask_winbind
  10. smb_krb5_locator_lookup

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

/* [<][>][^][v][top][bottom][index][help] */