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