sn2princ.c revision 4807:661e1fe142fa
1/*
2 * Copyright 2007 Sun Microsystems, Inc.  All rights reserved.
3 * Use is subject to license terms.
4 */
5
6#pragma ident	"%Z%%M%	%I%	%E% SMI"
7/*
8 * lib/krb5/os/sn2princ.c
9 *
10 * Copyright 1991,2002 by the Massachusetts Institute of Technology.
11 * All Rights Reserved.
12 *
13 * Export of this software from the United States of America may
14 *   require a specific license from the United States Government.
15 *   It is the responsibility of any person or organization contemplating
16 *   export to obtain such a license before exporting.
17 *
18 * WITHIN THAT CONSTRAINT, permission to use, copy, modify, and
19 * distribute this software and its documentation for any purpose and
20 * without fee is hereby granted, provided that the above copyright
21 * notice appear in all copies and that both that copyright notice and
22 * this permission notice appear in supporting documentation, and that
23 * the name of M.I.T. not be used in advertising or publicity pertaining
24 * to distribution of the software without specific, written prior
25 * permission.  Furthermore if you modify this software you must label
26 * your software as modified software and not distribute it in such a
27 * fashion that it might be confused with the original M.I.T. software.
28 * M.I.T. makes no representations about the suitability of
29 * this software for any purpose.  It is provided "as is" without express
30 * or implied warranty.
31 *
32 *
33 * Convert a hostname and service name to a principal in the "standard"
34 * form.
35 */
36
37#include <k5-int.h>
38#include "fake-addrinfo.h"
39#include <ctype.h>
40#include <netdb.h>
41#ifdef HAVE_SYS_PARAM_H
42#include <sys/param.h>
43#endif
44
45/*
46 * Solaris Kerberos:
47 * The following prototypes are needed because these are
48 * private interfaces that do not have prototypes in any .h
49 */
50extern struct hostent	*res_getipnodebyname(const char *, int, int, int *);
51extern struct hostent	*res_getipnodebyaddr(const void *, size_t, int, int *);
52extern void		res_freehostent(struct hostent *);
53
54/*
55 * Note, krb5_sname_to_principal() allocates memory for ret_princ.  Be sure to
56 * use krb5_free_principal() on ret_princ to free it when done referencing it.
57 */
58krb5_error_code KRB5_CALLCONV
59krb5_sname_to_principal(krb5_context context, const char *hostname, const char *sname, krb5_int32 type, krb5_principal *ret_princ)
60{
61    char **hrealms, *realm, *remote_host;
62    krb5_error_code retval;
63    register char *cp;
64    char localname[MAXHOSTNAMELEN];
65    /*
66     * Solaris Kerberos:
67     * Indicate whether or not to do a reverse lookup on a hostname for a host
68     * based principal.
69     */
70     int rev_lookup = NO_REV_LOOKUP;
71
72   KRB5_LOG0(KRB5_INFO, "krb5_sname_to_principal() start");
73#ifdef DEBUG_REFERRALS
74    printf("krb5_sname_to_principal(host=%s, sname=%s, type=%d)\n",hostname,sname,type);
75    printf("      name types: 0=unknown, 3=srv_host\n");
76#endif
77
78    if ((type == KRB5_NT_UNKNOWN) ||
79	(type == KRB5_NT_SRV_HST)) {
80
81	/* if hostname is NULL, use local hostname */
82	if (! hostname) {
83	    if (gethostname(localname, MAXHOSTNAMELEN)) {
84		KRB5_LOG0(KRB5_ERR, "krb5_sname_to_principal()"
85		       " gethostname failed");
86		return SOCKET_ERRNO;
87	    }
88	    hostname = localname;
89	}
90
91	/* if sname is NULL, use "host" */
92	if (! sname)
93	    sname = "host";
94
95	/* copy the hostname into non-volatile storage */
96	if (type == KRB5_NT_SRV_HST) {
97 	    /* Solaris Kerberos: don't want to do reverse lookup at this point
98 	     * as this will introduce a behavior change.  ifdef'ing this out in
99 	     * case we want to allow reverse lookup as a compile option.
100 	     */
101#ifdef KRB5_SNAME_TO_PRINCIPAL_REV_LOOKUP
102        rev_lookup = REV_LOOKUP;
103#endif
104		if (retval = krb5int_lookup_host(rev_lookup, hostname, &remote_host)) {
105            return retval;
106        }
107	} else /* type == KRB5_NT_UNKNOWN */ {
108	    remote_host = strdup((char *) hostname);
109	}
110	if (!remote_host)
111	    return ENOMEM;
112#ifdef DEBUG_REFERRALS
113 	printf("sname_to_princ: hostname <%s> after rdns processing\n",remote_host);
114#endif
115
116	if (type == KRB5_NT_SRV_HST)
117	    for (cp = remote_host; *cp; cp++)
118		if (isupper((int) *cp))
119		    *cp = tolower((int) *cp);
120
121	/*
122	 * Windows NT5's broken resolver gratuitously tacks on a
123	 * trailing period to the hostname (at least it does in
124	 * Beta2).  Find and remove it.
125	 */
126	if (remote_host[0]) {
127	    cp = remote_host + strlen(remote_host)-1;
128	    if (*cp == '.')
129		*cp = 0;
130	}
131
132	if (retval = krb5_get_host_realm(context, remote_host, &hrealms)) {
133	    free(remote_host);
134	    return retval;
135	}
136
137#ifdef DEBUG_REFERRALS
138	printf("sname_to_princ:  realm <%s> after krb5_get_host_realm\n",hrealms[0]);
139#endif
140
141	if (!hrealms[0]) {
142	    free(remote_host);
143	    krb5_xfree(hrealms);
144	    return KRB5_ERR_HOST_REALM_UNKNOWN;
145	}
146	realm = hrealms[0];
147
148	retval = krb5_build_principal(context, ret_princ, strlen(realm),
149				      realm, sname, remote_host,
150				      (char *)0);
151
152	krb5_princ_type(context, *ret_princ) = type;
153
154#ifdef DEBUG_REFERRALS
155	printf("krb5_sname_to_principal returning\n");
156	printf("realm: <%s>, sname: <%s>, remote_host: <%s>\n",
157	       realm,sname,remote_host);
158	krb5int_dbgref_dump_principal("krb5_sname_to_principal",*ret_princ);
159#endif
160
161	free(remote_host);
162
163	krb5_free_host_realm(context, hrealms);
164	return retval;
165    } else {
166	return KRB5_SNAME_UNSUPP_NAMETYPE;
167    }
168}
169
170/*
171 * Solaris Kerberos:
172 * Lookup a host in DNS. If hostname cannot be resolved in DNS return
173 * KRB5_ERR_BAD_HOSTNAME.
174 * If "rev_lookup" is non-zero a reverse lookup will be performed.
175 * "remote_host" will point to the resolved hostname.
176 * Code taken from krb5_sname_to_principal().
177 */
178krb5_error_code
179krb5int_lookup_host(int rev_lookup, const char *hostname, char **remote_host) {
180    struct hostent *hp;
181    struct hostent *hp2;
182    int addr_family;
183    int err;
184
185    /* Note that the old code would accept numeric addresses,
186       and if the gethostbyaddr step could convert them to
187       real hostnames, you could actually get reasonable
188       results.  If the mapping failed, you'd get dotted
189       triples as realm names.  *sigh*
190
191       The latter has been fixed in hst_realm.c, but we should
192       keep supporting numeric addresses if they do have
193       hostnames associated.  */
194
195    /*
196     * Solaris kerberos: using res_getipnodebyname() to force dns name
197     * resolution.  Note, res_getaddrinfo() isn't exported by libreolv
198     * so we use res_getipnodebyname() (MIT uses getaddrinfo()).
199     */
200    KRB5_LOG(KRB5_INFO, "krb5int_lookup_host() hostname %s",
201	    hostname);
202
203    addr_family = AF_INET;
204try_getipnodebyname_again:
205    hp = res_getipnodebyname(hostname, addr_family, 0, &err);
206    if (!hp) {
207	if (addr_family == AF_INET) {
208	    KRB5_LOG(KRB5_INFO, "krb5int_lookup_host()"
209		   " can't get AF_INET addr, err = %d", err);
210	    /* Just in case it's an IPv6-only name.  */
211	    addr_family = AF_INET6;
212	    goto try_getipnodebyname_again;
213	}
214	KRB5_LOG(KRB5_ERR, "krb5int_lookup_host()"
215	       " can't get AF_INET or AF_INET6 addr,"
216	       " err = %d", err);
217	return (KRB5_ERR_BAD_HOSTNAME);
218    }
219    *remote_host = strdup(hp ? hp->h_name : hostname);
220    if (!*remote_host) {
221	if (hp != NULL)
222	    res_freehostent(hp);
223	return ENOMEM;
224    }
225
226    if (rev_lookup) {
227       /*
228        * Do a reverse resolution to get the full name, just in
229        * case there's some funny business going on.  If there
230        * isn't an in-addr record, give up.
231        */
232       /* XXX: This is *so* bogus.  There are several cases where
233          this won't get us the canonical name of the host, but
234          this is what we've trained people to expect.  We'll
235          probably fix it at some point, but let's try to
236          preserve the current behavior and only shake things up
237          once when it comes time to fix this lossage.  */
238
239       hp2 = res_getipnodebyaddr(hp->h_addr, hp->h_length,
240       			hp->h_addrtype, &err);
241       if (hp != NULL) {
242       res_freehostent(hp);
243       hp = NULL;
244       }
245
246       if (hp2 != NULL) {
247       free(*remote_host);
248       *remote_host = strdup(hp2->h_name);
249       if (!*remote_host){
250           res_freehostent(hp2);
251           return ENOMEM;
252       }
253       KRB5_LOG(KRB5_INFO, "krb5int_lookup_host() remote_host %s",
254       	*remote_host);
255       res_freehostent(hp2);
256       hp2 = NULL;
257       }
258	}
259    return (0);
260}
261