1 /*
2  * Copyright 2009 Sun Microsystems, Inc.  All rights reserved.
3  * Use is subject to license terms.
4  */
5 
6 /*
7  * lib/krb5/krb/unparse.c
8  *
9  * Copyright 1990, 2008 by the Massachusetts Institute of Technology.
10  * All Rights Reserved.
11  *
12  * Export of this software from the United States of America may
13  *   require a specific license from the United States Government.
14  *   It is the responsibility of any person or organization contemplating
15  *   export to obtain such a license before exporting.
16  *
17  * WITHIN THAT CONSTRAINT, permission to use, copy, modify, and
18  * distribute this software and its documentation for any purpose and
19  * without fee is hereby granted, provided that the above copyright
20  * notice appear in all copies and that both that copyright notice and
21  * this permission notice appear in supporting documentation, and that
22  * the name of M.I.T. not be used in advertising or publicity pertaining
23  * to distribution of the software without specific, written prior
24  * permission.  Furthermore if you modify this software you must label
25  * your software as modified software and not distribute it in such a
26  * fashion that it might be confused with the original M.I.T. software.
27  * M.I.T. makes no representations about the suitability of
28  * this software for any purpose.  It is provided "as is" without express
29  * or implied warranty.
30  *
31  *
32  * krb5_unparse_name() routine
33  *
34  * Rewritten by Theodore Ts'o to properly unparse principal names
35  * which have the component or realm separator as part of one of their
36  * components.
37  */
38 
39 
40 #include "k5-int.h"
41 #ifndef _KERNEL
42 #include <stdio.h>
43 #endif
44 
45 /*
46  * SUNW17PACresync / Solaris Kerberos
47  * This realloc works for both Solaris kernel and user space.
48  */
49 void *
krb5int_realloc(void * oldp,size_t new_size,size_t old_size)50 krb5int_realloc(
51 		void *oldp,
52 		size_t new_size,
53 		size_t old_size)
54 {
55 #ifdef _KERNEL
56     char *newp = MALLOC(new_size);
57 
58     bcopy(oldp, newp, old_size < new_size ? old_size : new_size);
59     FREE(oldp, old_size);
60 
61     return (newp);
62 #else
63     return (realloc(oldp, new_size));
64 #endif
65 }
66 
67 /*
68  * converts the multi-part principal format used in the protocols to a
69  * single-string representation of the name.
70  *
71  * The name returned is in allocated storage and should be freed by
72  * the caller when finished.
73  *
74  * Conventions: / is used to separate components; @ is used to
75  * separate the realm from the rest of the name.  If '/', '@', or '\0'
76  * appear in any the component, they will be representing using
77  * backslash encoding.  ("\/", "\@", or '\0', respectively)
78  *
79  * returns error
80  *	KRB_PARSE_MALFORMED	principal is invalid (does not contain
81  *				at least 2 components)
82  * also returns system errors
83  *	ENOMEM			unable to allocate memory for string
84  */
85 
86 #define REALM_SEP	'@'
87 #define	COMPONENT_SEP	'/'
88 
89 static int
component_length_quoted(const krb5_data * src,int flags)90 component_length_quoted(const krb5_data *src, int flags)
91 {
92     const char *cp = src->data;
93     int length = src->length;
94     int j;
95     int size = length;
96 
97     if ((flags & KRB5_PRINCIPAL_UNPARSE_DISPLAY) == 0) {
98 	int no_realm = (flags & KRB5_PRINCIPAL_UNPARSE_NO_REALM) &&
99 		       !(flags & KRB5_PRINCIPAL_UNPARSE_SHORT);
100 
101 	for (j = 0; j < length; j++,cp++)
102 	    if ((!no_realm && *cp == REALM_SEP) ||
103 		*cp == COMPONENT_SEP ||
104 		*cp == '\0' || *cp == '\\' || *cp == '\t' ||
105 		*cp == '\n' || *cp == '\b')
106 		size++;
107     }
108 
109     return size;
110 }
111 
112 static int
copy_component_quoting(char * dest,const krb5_data * src,int flags)113 copy_component_quoting(char *dest, const krb5_data *src, int flags)
114 {
115     int j;
116     const char *cp = src->data;
117     char *q = dest;
118     int length = src->length;
119 
120     if (flags & KRB5_PRINCIPAL_UNPARSE_DISPLAY) {
121         (void) memcpy(dest, src->data, src->length);
122 	return src->length;
123     }
124 
125     for (j=0; j < length; j++,cp++) {
126 	int no_realm = (flags & KRB5_PRINCIPAL_UNPARSE_NO_REALM) &&
127 		       !(flags & KRB5_PRINCIPAL_UNPARSE_SHORT);
128 
129 	switch (*cp) {
130 	case REALM_SEP:
131 	    if (no_realm) {
132 		*q++ = *cp;
133 		break;
134 	    }
135 	/* FALLTHROUGH */
136 	case COMPONENT_SEP:
137 	case '\\':
138 	    *q++ = '\\';
139 	    *q++ = *cp;
140 	    break;
141 	case '\t':
142 	    *q++ = '\\';
143 	    *q++ = 't';
144 	    break;
145 	case '\n':
146 	    *q++ = '\\';
147 	    *q++ = 'n';
148 	    break;
149 	case '\b':
150 	    *q++ = '\\';
151 	    *q++ = 'b';
152 	    break;
153 #if 0
154 	/* Heimdal escapes spaces in principal names upon unparsing */
155 	case ' ':
156 	    *q++ = '\\';
157 	    *q++ = ' ';
158 	    break;
159 #endif
160 	case '\0':
161 	    *q++ = '\\';
162 	    *q++ = '0';
163 	    break;
164 	default:
165 	    *q++ = *cp;
166 	}
167     }
168     /*LINTED*/
169     return q - dest;
170 }
171 
172 static krb5_error_code
173 /*LINTED*/
k5_unparse_name(krb5_context context,krb5_const_principal principal,int flags,char ** name,unsigned int * size)174 k5_unparse_name(krb5_context context, krb5_const_principal principal,
175 		int flags, char **name, unsigned int *size)
176 {
177 #if 0
178 	/* SUNW17PACresync - lint - cp/length not used */
179         char *cp;
180         int	length;
181 #endif
182 	char *q;
183 	int i;
184 	krb5_int32 nelem;
185 	unsigned int totalsize = 0;
186 #ifndef _KERNEL
187 	/* SUNW17PACresync - princ in kernel will always have realm */
188 	char *default_realm = NULL;
189 #endif
190 	krb5_error_code ret = 0;
191 
192 	if (!principal || !name)
193 		return KRB5_PARSE_MALFORMED;
194 
195 #ifndef _KERNEL
196 	if (flags & KRB5_PRINCIPAL_UNPARSE_SHORT) {
197 		/* omit realm if local realm */
198 		krb5_principal_data p;
199 
200 		ret = krb5_get_default_realm(context, &default_realm);
201 		if (ret != 0)
202 			goto cleanup;
203 
204 		krb5_princ_realm(context, &p)->length = strlen(default_realm);
205 		krb5_princ_realm(context, &p)->data = default_realm;
206 
207 		if (krb5_realm_compare(context, &p, principal))
208 			flags |= KRB5_PRINCIPAL_UNPARSE_NO_REALM;
209 	}
210 #endif
211 	if ((flags & KRB5_PRINCIPAL_UNPARSE_NO_REALM) == 0) {
212 		totalsize += component_length_quoted(krb5_princ_realm(context,
213 								      principal),
214 						     flags);
215 		totalsize++;		/* This is for the separator */
216 	}
217 
218 	nelem = krb5_princ_size(context, principal);
219 	for (i = 0; i < (int) nelem; i++) {
220 #if 0
221 		/* SUNW17PACresync - lint - cp not used */
222 		cp = krb5_princ_component(context, principal, i)->data;
223 #endif
224 		totalsize += component_length_quoted(krb5_princ_component(context, principal, i), flags);
225 		totalsize++;	/* This is for the separator */
226 	}
227 	if (nelem == 0)
228 		totalsize++;
229 
230 	/*
231 	 * Allocate space for the ascii string; if space has been
232 	 * provided, use it, realloc'ing it if necessary.
233 	 *
234 	 * We need only n-1 seperators for n components, but we need
235 	 * an extra byte for the NUL at the end.
236 	 */
237 
238         if (size) {
239             if (*name && (*size < totalsize)) {
240 	        /* SUNW17PACresync - works for both kernel&user */
241 	        *name = krb5int_realloc(*name, totalsize, *size);
242             } else {
243                 *name = MALLOC(totalsize);
244             }
245             *size = totalsize;
246         } else {
247             *name = MALLOC(totalsize);
248         }
249 
250 	if (!*name) {
251 		ret = ENOMEM;
252 		goto cleanup;
253 	}
254 
255 	q = *name;
256 
257 	for (i = 0; i < (int) nelem; i++) {
258 #if 0
259 		/* SUNW17PACresync - lint - cp/length not used */
260 		cp = krb5_princ_component(context, principal, i)->data;
261 		length = krb5_princ_component(context, principal, i)->length;
262 #endif
263 		q += copy_component_quoting(q,
264 					    krb5_princ_component(context,
265 								 principal,
266 								 i),
267 					    flags);
268 		*q++ = COMPONENT_SEP;
269 	}
270 
271 	if (i > 0)
272 	    q--;		/* Back up last component separator */
273 	if ((flags & KRB5_PRINCIPAL_UNPARSE_NO_REALM) == 0) {
274 		*q++ = REALM_SEP;
275 		q += copy_component_quoting(q, krb5_princ_realm(context, principal), flags);
276 	}
277 	*q++ = '\0';
278 
279 cleanup:
280 #ifndef _KERNEL
281 	if (default_realm != NULL)
282 		krb5_free_default_realm(context, default_realm);
283 #endif
284 	return ret;
285 }
286 
287 krb5_error_code KRB5_CALLCONV
krb5_unparse_name(krb5_context context,krb5_const_principal principal,register char ** name)288 krb5_unparse_name(krb5_context context, krb5_const_principal principal, register char **name)
289 {
290     if (name != NULL)                      /* name == NULL will return error from _ext */
291 	*name = NULL;
292 
293     return k5_unparse_name(context, principal, 0, name, NULL);
294 }
295 
296 krb5_error_code KRB5_CALLCONV
krb5_unparse_name_ext(krb5_context context,krb5_const_principal principal,char ** name,unsigned int * size)297 krb5_unparse_name_ext(krb5_context context, krb5_const_principal principal,
298 		      char **name, unsigned int *size)
299 {
300     return k5_unparse_name(context, principal, 0, name, size);
301 }
302 
303 krb5_error_code KRB5_CALLCONV
krb5_unparse_name_flags(krb5_context context,krb5_const_principal principal,int flags,char ** name)304 krb5_unparse_name_flags(krb5_context context, krb5_const_principal principal,
305 			int flags, char **name)
306 {
307     if (name != NULL)
308 	*name = NULL;
309     return k5_unparse_name(context, principal, flags, name, NULL);
310 }
311 
312 krb5_error_code KRB5_CALLCONV
krb5_unparse_name_flags_ext(krb5_context context,krb5_const_principal principal,int flags,char ** name,unsigned int * size)313 krb5_unparse_name_flags_ext(krb5_context context, krb5_const_principal principal,
314 			    int flags, char **name, unsigned int *size)
315 {
316     return k5_unparse_name(context, principal, flags, name, size);
317 }
318 
319