2 * lib/krb5/os/hst_realm.c
4 * Copyright 1990,1991 by the Massachusetts Institute of Technology.
7 * Export of this software from the United States of America may
8 * require a specific license from the United States Government.
9 * It is the responsibility of any person or organization contemplating
10 * export to obtain such a license before exporting.
12 * WITHIN THAT CONSTRAINT, permission to use, copy, modify, and
13 * distribute this software and its documentation for any purpose and
14 * without fee is hereby granted, provided that the above copyright
15 * notice appear in all copies and that both that copyright notice and
16 * this permission notice appear in supporting documentation, and that
17 * the name of M.I.T. not be used in advertising or publicity pertaining
18 * to distribution of the software without specific, written prior
19 * permission. M.I.T. makes no representations about the suitability of
20 * this software for any purpose. It is provided "as is" without express
21 * or implied warranty.
24 * krb5_get_host_realm()
29 Figures out the Kerberos realm names for host, filling in a
30 pointer to an argv[] style list of names, terminated with a null pointer.
32 If host is NULL, the local host's realms are determined.
34 If there are no known realms for the host, the filled-in pointer is set
37 The pointer array and strings pointed to are all in allocated storage,
38 and should be freed by the caller when finished.
44 * Implementation notes:
46 * this implementation only provides one realm per host, using the same
47 * mapping file used in kerberos v4.
49 * Given a fully-qualified domain-style primary host name,
50 * return the name of the Kerberos realm for the host.
51 * If the hostname contains no discernable domain, or an error occurs,
52 * return the local realm name, as supplied by krb5_get_default_realm().
53 * If the hostname contains a domain, but no translation is found,
54 * the hostname's domain is converted to upper-case and returned.
56 * The format of each line of the translation file is:
57 * domain_name kerberos_realm
59 * host_name kerberos_realm
61 * domain_name should be of the form .XXX.YYY (e.g. .LCS.MIT.EDU)
62 * host names should be in the usual form (e.g. FOO.BAR.BAZ)
75 #ifdef KRB5_DNS_LOOKUP
79 #include <arpa/inet.h>
80 #include <arpa/nameser.h>
84 #endif /* KRB5_DNS_LOOKUP */
86 /* for old Unixes and friends ... */
87 #ifndef MAXHOSTNAMELEN
88 #define MAXHOSTNAMELEN 64
91 #define MAX_DNS_NAMELEN (15*(MAXHOSTNAMELEN + 1)+1)
93 #ifdef KRB5_DNS_LOOKUP
95 * Try to look up a TXT record pointing to a Kerberos realm
99 krb5_try_realm_txt_rr(prefix, name, realm)
100 const char *prefix, *name;
104 unsigned char bytes[2048];
108 char host[MAX_DNS_NAMELEN], *h;
110 int type, class, numanswers, numqueries, rdlen, len;
113 * Form our query, and send it via DNS
116 if (name == NULL || name[0] == '\0') {
119 if ( strlen(prefix) + strlen(name) + 3 > MAX_DNS_NAMELEN )
120 return KRB5_ERR_HOST_REALM_UNKNOWN;
121 sprintf(host,"%s.%s", prefix, name);
123 /* Realm names don't (normally) end with ".", but if the query
124 doesn't end with "." and doesn't get an answer as is, the
125 resolv code will try appending the local domain. Since the
126 realm names are absolutes, let's stop that. */
127 h = host + strlen (host);
128 if (h > host && h[-1] != '.')
131 size = res_search(host, C_IN, T_TXT, answer.bytes, sizeof(answer.bytes));
134 return KRB5_ERR_HOST_REALM_UNKNOWN;
138 numqueries = ntohs(answer.hdr.qdcount);
139 numanswers = ntohs(answer.hdr.ancount);
144 * We need to skip over the questions before we can get to the answers,
145 * which means we have to iterate over every query record. We use
146 * dn_expand to tell us how long each compressed name is.
149 #define INCR_CHECK(x, y) x += y; if (x > size + answer.bytes) \
150 return KRB5_ERR_HOST_REALM_UNKNOWN
151 #define CHECK(x, y) if (x + y > size + answer.bytes) \
152 return KRB5_ERR_HOST_REALM_UNKNOWN
153 #define NTOHSP(x, y) x[0] << 8 | x[1]; x += y
155 while (numqueries--) {
156 len = dn_expand(answer.bytes, answer.bytes + size, p, host,
159 return KRB5_ERR_HOST_REALM_UNKNOWN;
160 INCR_CHECK(p, len + 4); /* Name plus type plus class */
164 * We're now pointing at the answer records. Process the first
165 * TXT record we find.
168 while (numanswers--) {
170 /* First the name; use dn_expand to get the compressed size */
171 len = dn_expand(answer.bytes, answer.bytes + size, p,
174 return KRB5_ERR_HOST_REALM_UNKNOWN;
177 /* Next is the query type */
181 /* Next is the query class; also skip over 4 byte TTL */
185 /* Record data length - make sure we aren't truncated */
190 if (p + rdlen > answer.bytes + size)
191 return KRB5_ERR_HOST_REALM_UNKNOWN;
194 * If this is a TXT record, return the string. Note that the
195 * string has a 1-byte length in the front
197 /* XXX What about flagging multiple TXT records as an error? */
199 if (class == C_IN && type == T_TXT) {
201 if (p + len > answer.bytes + size)
202 return KRB5_ERR_HOST_REALM_UNKNOWN;
203 *realm = malloc(len + 1);
206 strncpy(*realm, (char *) p, len);
207 (*realm)[len] = '\0';
208 /* Avoid a common error. */
209 if ( (*realm)[len-1] == '.' )
210 (*realm)[len-1] = '\0';
215 return KRB5_ERR_HOST_REALM_UNKNOWN;
217 #endif /* KRB5_DNS_LOOKUP */
220 KRB5_DLLIMP krb5_error_code KRB5_CALLCONV
221 krb5_get_host_realm(context, host, realmsp)
222 krb5_context context;
223 const char FAR *host;
224 char FAR * FAR * FAR *realmsp;
227 char *default_realm, *realm, *cp;
228 krb5_error_code retval;
230 char local_host[MAX_DNS_NAMELEN+1];
233 strncpy(local_host, host, MAX_DNS_NAMELEN);
235 if (gethostname(local_host, sizeof(local_host)-1) == -1)
238 local_host[MAX_DNS_NAMELEN] = '\0';
239 for (cp = local_host; *cp; cp++) {
243 l = strlen(local_host);
244 /* strip off trailing dot */
245 if (l && local_host[l-1] == '.')
249 Search for the best match for the host or domain.
250 Example: Given a host a.b.c.d, try to match on:
261 realm = default_realm = (char *)NULL;
263 retval = profile_get_string(context->profile, "domain_realm", cp,
264 0, (char *)NULL, &realm);
267 if (realm != (char *)NULL)
268 break; /* Match found */
270 /* Setup for another test */
273 if (default_realm == (char *)NULL) {
274 /* If nothing else works, use the host's domain */
278 cp = strchr(cp, '.');
282 #ifdef KRB5_DNS_LOOKUP
283 if (realm == (char *)NULL) {
285 * Since this didn't appear in our config file, try looking
286 * it up via DNS. Look for a TXT records of the form:
288 * _kerberos.<hostname>
289 * _kerberos.<searchlist>
290 * _kerberos.<defaultrealm>
295 retval = krb5_try_realm_txt_rr("_kerberos", cp, &realm);
299 } while (retval && cp && cp[0]);
301 retval = krb5_try_realm_txt_rr("_kerberos", "", &realm);
302 if (retval && default_realm) {
305 retval = krb5_try_realm_txt_rr("_kerberos", cp, &realm);
309 } while (retval && cp && cp[0]);
312 #endif /* KRB5_DNS_LOOKUP */
313 if (realm == (char *)NULL) {
314 if (default_realm != (char *)NULL) {
315 /* We are defaulting to the realm of the host */
316 if (!(cp = (char *)malloc(strlen(default_realm)+1)))
318 strcpy(cp, default_realm);
321 /* Assume the realm name is upper case */
322 for (cp = realm; *cp; cp++)
326 /* We are defaulting to the local realm */
327 retval = krb5_get_default_realm(context, &realm);
333 if (!(retrealms = (char **)calloc(2, sizeof(*retrealms)))) {
334 if (realm != (char *)NULL)
339 retrealms[0] = realm;
342 *realmsp = retrealms;