1 #pragma ident "%Z%%M% %I% %E% SMI" 2 3 /* 4 * lib/krb5/os/hst_realm.c 5 * 6 * Copyright 1990,1991,2002 by the Massachusetts Institute of Technology. 7 * All Rights Reserved. 8 * 9 * Export of this software from the United States of America may 10 * require a specific license from the United States Government. 11 * It is the responsibility of any person or organization contemplating 12 * export to obtain such a license before exporting. 13 * 14 * WITHIN THAT CONSTRAINT, permission to use, copy, modify, and 15 * distribute this software and its documentation for any purpose and 16 * without fee is hereby granted, provided that the above copyright 17 * notice appear in all copies and that both that copyright notice and 18 * this permission notice appear in supporting documentation, and that 19 * the name of M.I.T. not be used in advertising or publicity pertaining 20 * to distribution of the software without specific, written prior 21 * permission. Furthermore if you modify this software you must label 22 * your software as modified software and not distribute it in such a 23 * fashion that it might be confused with the original M.I.T. software. 24 * M.I.T. makes no representations about the suitability of 25 * this software for any purpose. It is provided "as is" without express 26 * or implied warranty. 27 * 28 * 29 * krb5_get_host_realm() 30 */ 31 32 33 /* 34 Figures out the Kerberos realm names for host, filling in a 35 pointer to an argv[] style list of names, terminated with a null pointer. 36 37 If host is NULL, the local host's realms are determined. 38 39 If there are no known realms for the host, the filled-in pointer is set 40 to NULL. 41 42 The pointer array and strings pointed to are all in allocated storage, 43 and should be freed by the caller when finished. 44 45 returns system errors 46 */ 47 48 /* 49 * Implementation notes: 50 * 51 * this implementation only provides one realm per host, using the same 52 * mapping file used in kerberos v4. 53 54 * Given a fully-qualified domain-style primary host name, 55 * return the name of the Kerberos realm for the host. 56 * If the hostname contains no discernable domain, or an error occurs, 57 * return the local realm name, as supplied by krb5_get_default_realm(). 58 * If the hostname contains a domain, but no translation is found, 59 * the hostname's domain is converted to upper-case and returned. 60 * 61 * The format of each line of the translation file is: 62 * domain_name kerberos_realm 63 * -or- 64 * host_name kerberos_realm 65 * 66 * domain_name should be of the form .XXX.YYY (e.g. .LCS.MIT.EDU) 67 * host names should be in the usual form (e.g. FOO.BAR.BAZ) 68 */ 69 70 #define NEED_SOCKETS 71 #include "k5-int.h" 72 #include "os-proto.h" 73 #include <ctype.h> 74 #include <stdio.h> 75 #ifdef HAVE_STRING_H 76 #include <string.h> 77 #else 78 #include <strings.h> 79 #endif 80 81 #ifdef KRB5_DNS_LOOKUP 82 #ifdef WSHELPER 83 #include <wshelper.h> 84 #else /* WSHELPER */ 85 #include <arpa/inet.h> 86 #include <arpa/nameser.h> 87 #ifndef T_TXT /* not defined on SunOS 4 */ 88 # define T_TXT 15 89 #endif 90 #include <resolv.h> 91 #include <netdb.h> 92 #endif /* WSHELPER */ 93 #endif /* KRB5_DNS_LOOKUP */ 94 95 #include <fake-addrinfo.h> 96 97 #ifdef KRB5_DNS_LOOKUP 98 99 #include "dnsglue.h" 100 101 /* 102 * Try to look up a TXT record pointing to a Kerberos realm 103 */ 104 105 krb5_error_code 106 krb5_try_realm_txt_rr(const char *prefix, const char *name, char **realm) 107 { 108 krb5_error_code retval = KRB5_ERR_HOST_REALM_UNKNOWN; 109 const unsigned char *p, *base; 110 char host[MAXDNAME], *h; 111 int ret, rdlen, len; 112 struct krb5int_dns_state *ds = NULL; 113 114 /* 115 * Form our query, and send it via DNS 116 */ 117 118 if (name == NULL || name[0] == '\0') { 119 if (strlen (prefix) >= sizeof(host)-1) 120 return KRB5_ERR_HOST_REALM_UNKNOWN; 121 strcpy(host,prefix); 122 } else { 123 if ( strlen(prefix) + strlen(name) + 3 > MAXDNAME ) 124 return KRB5_ERR_HOST_REALM_UNKNOWN; 125 /*LINTED*/ 126 sprintf(host,"%s.%s", prefix, name); 127 128 /* Realm names don't (normally) end with ".", but if the query 129 doesn't end with "." and doesn't get an answer as is, the 130 resolv code will try appending the local domain. Since the 131 realm names are absolutes, let's stop that. 132 133 But only if a name has been specified. If we are performing 134 a search on the prefix alone then the intention is to allow 135 the local domain or domain search lists to be expanded. 136 */ 137 138 h = host + strlen (host); 139 if ((h > host) && (h[-1] != '.') && ((h - host + 1) < sizeof(host))) 140 strcpy (h, "."); 141 } 142 ret = krb5int_dns_init(&ds, host, C_IN, T_TXT); 143 if (ret < 0) 144 goto errout; 145 146 ret = krb5int_dns_nextans(ds, &base, &rdlen); 147 if (ret < 0 || base == NULL) 148 goto errout; 149 150 p = base; 151 if (!INCR_OK(base, rdlen, p, 1)) 152 goto errout; 153 154 len = *p++; 155 *realm = malloc((size_t)len + 1); 156 if (*realm == NULL) { 157 retval = ENOMEM; 158 goto errout; 159 } 160 strncpy(*realm, (const char *)p, (size_t)len); 161 (*realm)[len] = '\0'; 162 /* Avoid a common error. */ 163 if ( (*realm)[len-1] == '.' ) 164 (*realm)[len-1] = '\0'; 165 retval = 0; 166 167 errout: 168 if (ds != NULL) { 169 krb5int_dns_fini(ds); 170 ds = NULL; 171 } 172 return retval; 173 } 174 #else /* KRB5_DNS_LOOKUP */ 175 #ifndef MAXDNAME 176 #define MAXDNAME (16 * MAXHOSTNAMELEN) 177 #endif /* MAXDNAME */ 178 #endif /* KRB5_DNS_LOOKUP */ 179 180 181 krb5_error_code krb5int_translate_gai_error (int); 182 183 static krb5_error_code 184 krb5int_get_fq_hostname (char *buf, size_t bufsize, const char *name) 185 { 186 struct addrinfo *ai, hints; 187 int err; 188 189 memset (&hints, 0, sizeof (hints)); 190 hints.ai_family = AF_UNSPEC; 191 hints.ai_flags = AI_CANONNAME; 192 err = getaddrinfo (name, 0, &hints, &ai); 193 if (err) 194 return krb5int_translate_gai_error (err); 195 if (ai->ai_canonname == 0) 196 return KRB5_EAI_FAIL; 197 strncpy (buf, ai->ai_canonname, bufsize); 198 buf[bufsize-1] = 0; 199 freeaddrinfo (ai); 200 return 0; 201 } 202 203 /* Get the local host name, try to make it fully-qualified. 204 Always return a null-terminated string. 205 Might return an error if gethostname fails. */ 206 krb5_error_code 207 krb5int_get_fq_local_hostname (char *buf, size_t bufsiz) 208 { 209 buf[0] = 0; 210 if (gethostname (buf, bufsiz) == -1) 211 return SOCKET_ERRNO; 212 buf[bufsiz - 1] = 0; 213 return krb5int_get_fq_hostname (buf, bufsiz, buf); 214 } 215 216 krb5_error_code KRB5_CALLCONV 217 krb5_get_host_realm(krb5_context context, const char *host, char ***realmsp) 218 { 219 char **retrealms; 220 char *default_realm, *realm, *cp, *temp_realm; 221 krb5_error_code retval; 222 int l; 223 char local_host[MAXDNAME+1]; 224 225 if (host) { 226 /* Filter out numeric addresses if the caller utterly failed to 227 convert them to names. */ 228 /* IPv4 - dotted quads only */ 229 if (strspn(host, "01234567890.") == strlen(host)) { 230 /* All numbers and dots... if it's three dots, it's an 231 IP address, and we reject it. But "12345" could be 232 a local hostname, couldn't it? We'll just assume 233 that a name with three dots is not meant to be an 234 all-numeric hostname three all-numeric domains down 235 from the current domain. */ 236 int ndots = 0; 237 const char *p; 238 for (p = host; *p; p++) 239 if (*p == '.') 240 ndots++; 241 if (ndots == 3) 242 return KRB5_ERR_NUMERIC_REALM; 243 } 244 if (strchr(host, ':')) 245 /* IPv6 numeric address form? Bye bye. */ 246 return KRB5_ERR_NUMERIC_REALM; 247 248 /* Should probably error out if strlen(host) > MAX_DNS_NAMELEN. */ 249 strncpy(local_host, host, sizeof(local_host)); 250 local_host[sizeof(local_host) - 1] = '\0'; 251 } else { 252 retval = krb5int_get_fq_local_hostname (local_host, 253 sizeof (local_host)); 254 if (retval) 255 return retval; 256 } 257 258 for (cp = local_host; *cp; cp++) { 259 if (isupper((int) (*cp))) 260 *cp = tolower((int) *cp); 261 } 262 l = strlen(local_host); 263 /* strip off trailing dot */ 264 if (l && local_host[l-1] == '.') 265 local_host[l-1] = 0; 266 267 /* 268 Search for the best match for the host or domain. 269 Example: Given a host a.b.c.d, try to match on: 270 1) A.B.C.D 271 2) .B.C.D 272 3) B.C.D 273 4) .C.D 274 5) C.D 275 6) .D 276 7) D 277 */ 278 279 cp = local_host; 280 realm = default_realm = (char *)NULL; 281 temp_realm = 0; 282 while (cp) { 283 retval = profile_get_string(context->profile, "domain_realm", cp, 284 0, (char *)NULL, &temp_realm); 285 if (retval) 286 return retval; 287 if (temp_realm != (char *)NULL) 288 break; /* Match found */ 289 290 /* Setup for another test */ 291 if (*cp == '.') { 292 cp++; 293 if (default_realm == (char *)NULL) { 294 /* If nothing else works, use the host's domain */ 295 default_realm = cp; 296 } 297 } else { 298 cp = strchr(cp, '.'); 299 } 300 } 301 if (temp_realm) { 302 realm = malloc(strlen(temp_realm) + 1); 303 if (!realm) { 304 profile_release_string(temp_realm); 305 return ENOMEM; 306 } 307 strcpy(realm, temp_realm); 308 profile_release_string(temp_realm); 309 } 310 311 #ifdef KRB5_DNS_LOOKUP 312 if (realm == (char *)NULL) { 313 int use_dns = _krb5_use_dns_realm(context); 314 if ( use_dns ) { 315 /* 316 * Since this didn't appear in our config file, try looking 317 * it up via DNS. Look for a TXT records of the form: 318 * 319 * _kerberos.<hostname> 320 * 321 */ 322 cp = local_host; 323 do { 324 retval = krb5_try_realm_txt_rr("_kerberos", cp, &realm); 325 cp = strchr(cp,'.'); 326 if (cp) 327 cp++; 328 } while (retval && cp && cp[0]); 329 } 330 } 331 #endif /* KRB5_DNS_LOOKUP */ 332 if (realm == (char *)NULL) { 333 if (default_realm != (char *)NULL) { 334 /* We are defaulting to the realm of the host */ 335 if (!(cp = (char *)malloc(strlen(default_realm)+1))) 336 return ENOMEM; 337 strcpy(cp, default_realm); 338 realm = cp; 339 340 /* Assume the realm name is upper case */ 341 for (cp = realm; *cp; cp++) 342 if (islower((int) (*cp))) 343 *cp = toupper((int) *cp); 344 } else { 345 /* We are defaulting to the local realm */ 346 retval = krb5_get_default_realm(context, &realm); 347 if (retval) { 348 return retval; 349 } 350 } 351 } 352 if (!(retrealms = (char **)calloc(2, sizeof(*retrealms)))) { 353 if (realm != (char *)NULL) 354 free(realm); 355 return ENOMEM; 356 } 357 358 retrealms[0] = realm; 359 retrealms[1] = 0; 360 361 *realmsp = retrealms; 362 return 0; 363 } 364 365 #if defined(_WIN32) && !defined(__CYGWIN32__) 366 # ifndef EAFNOSUPPORT 367 # define EAFNOSUPPORT WSAEAFNOSUPPORT 368 # endif 369 #endif 370 371 krb5_error_code 372 krb5int_translate_gai_error (int num) 373 { 374 switch (num) { 375 #ifdef EAI_ADDRFAMILY 376 case EAI_ADDRFAMILY: 377 return EAFNOSUPPORT; 378 #endif 379 case EAI_AGAIN: 380 return EAGAIN; 381 case EAI_BADFLAGS: 382 return EINVAL; 383 case EAI_FAIL: 384 return KRB5_EAI_FAIL; 385 case EAI_FAMILY: 386 return EAFNOSUPPORT; 387 case EAI_MEMORY: 388 return ENOMEM; 389 #if EAI_NODATA != EAI_NONAME 390 case EAI_NODATA: 391 return KRB5_EAI_NODATA; 392 #endif 393 case EAI_NONAME: 394 return KRB5_EAI_NONAME; 395 case EAI_SERVICE: 396 return KRB5_EAI_SERVICE; 397 case EAI_SOCKTYPE: 398 return EINVAL; 399 #ifdef EAI_SYSTEM 400 case EAI_SYSTEM: 401 return errno; 402 #endif 403 } 404 /* abort (); */ 405 return -1; 406 } 407