xref: /illumos-gate/usr/src/lib/gss_mechs/mech_krb5/krb5/os/sn2princ.c (revision 80ab886d233f514d54c2a6bdeb9fdfd951bd6881)
1 /*
2  * Copyright 2005 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 #define NEED_SOCKETS
38 #include <k5-int.h>
39 #include "fake-addrinfo.h"
40 #include <ctype.h>
41 #include <netdb.h>
42 #ifdef HAVE_SYS_PARAM_H
43 #include <sys/param.h>
44 #endif
45 
46 /* Solaris Kerberos: the following prototypes are needed because these are
47  * private interfaces that do not have prototypes in any .h
48  */
49 extern struct hostent	*res_getipnodebyname(const char *, int, int, int *);
50 extern struct hostent	*res_getipnodebyaddr(const void *, size_t, int, int *);
51 extern void		res_freehostent(struct hostent *);
52 
53 /*
54  * Note, krb5_sname_to_principal() allocates memory for ret_princ.  Be sure to
55  * use krb5_free_principal() on ret_princ to free it when done referencing it.
56  */
57 krb5_error_code KRB5_CALLCONV
58 krb5_sname_to_principal(krb5_context context, const char *hostname, const char *sname, krb5_int32 type, krb5_principal *ret_princ)
59 {
60     char **hrealms, *realm, *remote_host;
61     krb5_error_code retval;
62     register char *cp;
63     char localname[MAXHOSTNAMELEN];
64 
65     KRB5_LOG0(KRB5_INFO, "krb5_sname_to_principal() start");
66     if ((type == KRB5_NT_UNKNOWN) ||
67 	(type == KRB5_NT_SRV_HST)) {
68 
69 	/* if hostname is NULL, use local hostname */
70 	if (! hostname) {
71 	    if (gethostname(localname, MAXHOSTNAMELEN)) {
72 		KRB5_LOG0(KRB5_ERR, "krb5_sname_to_principal()"
73 		       " gethostname failed");
74 		return SOCKET_ERRNO;
75 	    }
76 	    hostname = localname;
77 	}
78 
79 	/* if sname is NULL, use "host" */
80 	if (! sname)
81 	    sname = "host";
82 
83 	/* copy the hostname into non-volatile storage */
84 	if (type == KRB5_NT_SRV_HST) {
85 	    struct hostent *hp;
86 #ifdef KRB5_SNAME_TO_PRINCIPAL_REV_LOOKUP
87 	    struct hostent *hp2;
88 #endif
89 	    int addr_family;
90 	    int err;
91 
92 	    /* Note that the old code would accept numeric addresses,
93 	       and if the gethostbyaddr step could convert them to
94 	       real hostnames, you could actually get reasonable
95 	       results.  If the mapping failed, you'd get dotted
96 	       triples as realm names.  *sigh*
97 
98 	       The latter has been fixed in hst_realm.c, but we should
99 	       keep supporting numeric addresses if they do have
100 	       hostnames associated.  */
101 
102 	    /*
103 	     * Solaris kerberos: using res_getipnodebyname() to force dns name
104 	     * resolution.  Note, res_getaddrinfo() isn't exported by libreolv
105 	     * so we use res_getipnodebyname() (MIT uses getaddrinfo()).
106 	     */
107 	    KRB5_LOG(KRB5_INFO, "krb5_sname_to_principal() hostname %s",
108 		    hostname);
109 
110 	    addr_family = AF_INET;
111 	try_getipnodebyname_again:
112 	    hp = res_getipnodebyname(hostname, addr_family, 0, &err);
113 	    if (!hp) {
114 		if (addr_family == AF_INET) {
115 		    KRB5_LOG(KRB5_INFO, "krb5_sname_to_principal()"
116 			   " can't get AF_INET addr, err = %d", err);
117 		    /* Just in case it's an IPv6-only name.  */
118 		    addr_family = AF_INET6;
119 		    goto try_getipnodebyname_again;
120 		}
121 		KRB5_LOG(KRB5_ERR, "krb5_sname_to_principal()"
122 		       " can't get AF_INET or AF_INET6 addr,"
123 		       " err = %d", err);
124 		return (KRB5_ERR_BAD_HOSTNAME);
125 	    }
126 	    remote_host = strdup(hp ? hp->h_name : hostname);
127 	    if (!remote_host) {
128 		if (hp != NULL)
129 		    res_freehostent(hp);
130 		return ENOMEM;
131 	    }
132 
133 	    /*
134 	     * Solaris Kerberos: don't want to do reverse lookup at this point
135 	     * as this will introduce a behavior change.  ifdef'ing this out in
136 	     * case we want to allow reverse lookup as a compile option.
137 	     */
138 #ifdef KRB5_SNAME_TO_PRINCIPAL_REV_LOOKUP
139 	    /*
140 	     * Do a reverse resolution to get the full name, just in
141 	     * case there's some funny business going on.  If there
142 	     * isn't an in-addr record, give up.
143 	     */
144 	    /* XXX: This is *so* bogus.  There are several cases where
145 	       this won't get us the canonical name of the host, but
146 	       this is what we've trained people to expect.  We'll
147 	       probably fix it at some point, but let's try to
148 	       preserve the current behavior and only shake things up
149 	       once when it comes time to fix this lossage.  */
150 
151 	    hp2 = res_getipnodebyaddr(hp->h_addr, hp->h_length,
152 					hp->h_addrtype, &err);
153 	    if (hp != NULL) {
154 		res_freehostent(hp);
155 		hp = NULL;
156 	    }
157 
158 	    if (hp2 != NULL) {
159 		free(remote_host);
160 		remote_host = strdup(hp2->h_name);
161 		if (!remote_host){
162 		    res_freehostent(hp2);
163 		    return ENOMEM;
164 		}
165 		KRB5_LOG(KRB5_INFO, "krb5_sname_to_principal() remote_host %s",
166 			remote_host);
167 		res_freehostent(hp2);
168 		hp2 = NULL;
169 	    }
170 
171 #endif /* KRB5_SNAME_TO_PRINCIPAL_REV_LOOKUP */
172 
173 	} else /* type == KRB5_NT_UNKNOWN */ {
174 	    remote_host = strdup((char *) hostname);
175 	}
176 	if (!remote_host)
177 	    return ENOMEM;
178 
179 
180 	if (type == KRB5_NT_SRV_HST)
181 	    for (cp = remote_host; *cp; cp++)
182 		if (isupper((int) *cp))
183 		    *cp = tolower((int) *cp);
184 
185 	/*
186 	 * Windows NT5's broken resolver gratuitously tacks on a
187 	 * trailing period to the hostname (at least it does in
188 	 * Beta2).  Find and remove it.
189 	 */
190 	if (remote_host[0]) {
191 	    cp = remote_host + strlen(remote_host)-1;
192 	    if (*cp == '.')
193 		*cp = 0;
194 	}
195 
196 	if (retval = krb5_get_host_realm(context, remote_host, &hrealms)) {
197 	    free(remote_host);
198 	    return retval;
199 	}
200 	if (!hrealms[0]) {
201 	    free(remote_host);
202 	    krb5_xfree(hrealms);
203 	    return KRB5_ERR_HOST_REALM_UNKNOWN;
204 	}
205 	realm = hrealms[0];
206 
207 	retval = krb5_build_principal(context, ret_princ, strlen(realm),
208 				      realm, sname, remote_host,
209 				      (char *)0);
210 
211 	krb5_princ_type(context, *ret_princ) = type;
212 
213 	free(remote_host);
214 
215 	krb5_free_host_realm(context, hrealms);
216 	return retval;
217     } else {
218 	return KRB5_SNAME_UNSUPP_NAMETYPE;
219     }
220 }
221 
222