xref: /illumos-gate/usr/src/lib/libgss/g_dsp_status.c (revision 1f5207b7604fb44407eb4342aff613f7c4508508)
1 /*
2  * CDDL HEADER START
3  *
4  * The contents of this file are subject to the terms of the
5  * Common Development and Distribution License (the "License").
6  * You may not use this file except in compliance with the License.
7  *
8  * You can obtain a copy of the license at usr/src/OPENSOLARIS.LICENSE
9  * or http://www.opensolaris.org/os/licensing.
10  * See the License for the specific language governing permissions
11  * and limitations under the License.
12  *
13  * When distributing Covered Code, include this CDDL HEADER in each
14  * file and include the License file at usr/src/OPENSOLARIS.LICENSE.
15  * If applicable, add the following below this CDDL HEADER, with the
16  * fields enclosed by brackets "[]" replaced with your own identifying
17  * information: Portions Copyright [yyyy] [name of copyright owner]
18  *
19  * CDDL HEADER END
20  */
21 /*
22  * Copyright (c) 1999, 2010, Oracle and/or its affiliates. All rights reserved.
23  */
24 
25 /*
26  *  glue routine gss_display_status
27  *
28  */
29 
30 #include <mechglueP.h>
31 #include "gssapiP_generic.h"
32 #include <stdio.h>
33 #ifdef HAVE_STDLIB_H
34 #include <stdlib.h>
35 #endif
36 #include <string.h>
37 #include <libintl.h>
38 #include <errno.h>
39 #include <syslog.h>
40 #ifndef TEXT_DOMAIN
41 #error TEXT_DOMAIN not defined
42 #endif
43 
44 /* local function */
45 static OM_uint32 displayMajor(OM_uint32, OM_uint32 *, gss_buffer_t);
46 
47 
48 OM_uint32
49 gss_display_status(minor_status,
50 			status_value,
51 			status_type,
52 			req_mech_type,
53 			message_context,
54 			status_string)
55 
56 OM_uint32 *minor_status;
57 OM_uint32 status_value;
58 int status_type;
59 const gss_OID req_mech_type;
60 OM_uint32 *message_context;
61 gss_buffer_t status_string;
62 {
63 	gss_OID			mech_type = (gss_OID) req_mech_type;
64 	gss_mechanism		mech;
65 	gss_OID_desc		m_oid = { 0, 0 };
66 
67 	if (minor_status != NULL)
68 		*minor_status = 0;
69 
70 	if (status_string != GSS_C_NO_BUFFER) {
71 		status_string->length = 0;
72 		status_string->value = NULL;
73 	}
74 
75 	if (minor_status == NULL ||
76 	    message_context == NULL ||
77 	    status_string == GSS_C_NO_BUFFER)
78 		return (GSS_S_CALL_INACCESSIBLE_WRITE);
79 
80 	/* we handle major status codes, and the mechs do the minor */
81 	if (status_type == GSS_C_GSS_CODE)
82 		return (displayMajor(status_value, message_context,
83 				status_string));
84 
85 	/*
86 	 * must be the minor status - let mechs do the work
87 	 * select the appropriate underlying mechanism routine and
88 	 * call it.
89 	 */
90 
91 	/* In this version, we only handle status codes that have been
92 	   mapped to a flat numbering space.  Look up the value we got
93 	   passed.  If it's not found, complain.  */
94 	if (status_value == 0) {
95 		status_string->value = strdup("Unknown error");
96 		if (status_string->value == NULL) {
97 			*minor_status = ENOMEM;
98 			map_errcode(minor_status);
99 			return GSS_S_FAILURE;
100 		}
101 		status_string->length = strlen(status_string->value);
102 		*message_context = 0;
103 		*minor_status = 0;
104 		return GSS_S_COMPLETE;
105 	}
106 	{
107 		int err;
108 	        OM_uint32 m_status = 0, status;
109 
110 		err = gssint_mecherrmap_get(status_value, &m_oid, &m_status);
111 		if (err) {
112 			*minor_status = err;
113 			map_errcode(minor_status);
114 			return GSS_S_BAD_STATUS;
115 		}
116 
117 		if (m_oid.length == 0) {
118 			/* Magic flag for com_err values.  */
119 			status = gssint_g_display_com_err_status(minor_status,
120 							m_status,
121 							status_string);
122 			if (status != GSS_S_COMPLETE)
123 				map_errcode(minor_status);
124 			return status;
125 		}
126 		mech_type = &m_oid;
127 		status_value = m_status;
128 	}
129 
130 	mech = __gss_get_mechanism(mech_type);
131 
132 	if (mech && mech->gss_display_status) {
133 		OM_uint32 r;
134 
135 		if (mech_type == GSS_C_NULL_OID)
136 			mech_type = &mech->mech_type;
137 
138 		r = mech->gss_display_status(mech->context, minor_status,
139 				status_value, status_type, mech_type,
140 				message_context, status_string);
141 		/* How's this for weird?  If we get an error returning the
142 		mechanism-specific error code, we save away the
143 		mechanism-specific error code describing the error.  */
144 		if (r != GSS_S_COMPLETE)
145 			map_error(minor_status, mech);
146 		return r;
147 	}
148 
149 	if (!mech)
150 		return (GSS_S_BAD_MECH);
151 
152 	return (GSS_S_UNAVAILABLE);
153 } /* gss_display_status */
154 
155 
156 /*
157  * function to map the major error codes
158  * it uses case statements so that the strings could be wrapped by gettext
159  * msgCtxt is interpreted as:
160  *	0 - first call
161  *	1 - routine error
162  *	>= 2 - the supplementary error code bit shifted by 1
163  */
164 static OM_uint32
165 displayMajor(status, msgCtxt, outStr)
166 OM_uint32 status;
167 OM_uint32 *msgCtxt;
168 gss_buffer_t outStr;
169 {
170 	OM_uint32 oneVal, mask = 0x1, currErr;
171 	char *errStr = NULL;
172 	int i, haveErr = 0;
173 
174 	/* take care of the success value first */
175 	if (status == GSS_S_COMPLETE)
176 		errStr = dgettext(TEXT_DOMAIN,
177 				"The routine completed successfully");
178 	else if (*msgCtxt == 0 && (oneVal = GSS_CALLING_ERROR(status))) {
179 		switch (oneVal) {
180 		case GSS_S_CALL_INACCESSIBLE_READ:
181 			errStr = dgettext(TEXT_DOMAIN,
182 					"A required input parameter"
183 					" could not be read");
184 			break;
185 
186 		case GSS_S_CALL_INACCESSIBLE_WRITE:
187 			errStr = dgettext(TEXT_DOMAIN,
188 					"A required output parameter"
189 					" could not be written");
190 			break;
191 
192 		case GSS_S_CALL_BAD_STRUCTURE:
193 			errStr = dgettext(TEXT_DOMAIN,
194 					"A parameter was malformed");
195 			break;
196 
197 		default:
198 			errStr = dgettext(TEXT_DOMAIN,
199 					"An invalid status code was supplied");
200 			break;
201 		}
202 
203 		/* we now need to determine new value of msgCtxt */
204 		if (GSS_ROUTINE_ERROR(status))
205 			*msgCtxt = 1;
206 		else if ((oneVal = GSS_SUPPLEMENTARY_INFO(status)) != 0)
207 			*msgCtxt = (OM_uint32)(oneVal << 1);
208 		else
209 			*msgCtxt = 0;
210 
211 	} else if ((*msgCtxt == 0 || *msgCtxt == 1) &&
212 		(oneVal = GSS_ROUTINE_ERROR(status))) {
213 		switch (oneVal) {
214 		case GSS_S_BAD_MECH:
215 			errStr = dgettext(TEXT_DOMAIN,
216 					"An unsupported mechanism"
217 					" was requested");
218 			break;
219 
220 		case GSS_S_BAD_NAME:
221 			errStr = dgettext(TEXT_DOMAIN,
222 					"An invalid name was supplied");
223 			break;
224 
225 		case GSS_S_BAD_NAMETYPE:
226 			errStr = dgettext(TEXT_DOMAIN,
227 					"A supplied name was of an"
228 					" unsupported type");
229 			break;
230 
231 		case GSS_S_BAD_BINDINGS:
232 			errStr = dgettext(TEXT_DOMAIN,
233 					"Incorrect channel bindings"
234 					" were supplied");
235 			break;
236 
237 		case GSS_S_BAD_SIG: /* same as GSS_S_BAD_MIC: */
238 			errStr = dgettext(TEXT_DOMAIN,
239 					"A token had an invalid Message"
240 					" Integrity Check (MIC)");
241 			break;
242 
243 		case GSS_S_NO_CRED:
244 			errStr = dgettext(TEXT_DOMAIN,
245 					"No credentials were supplied, or the"
246 					" credentials were unavailable or"
247 					" inaccessible");
248 			break;
249 
250 		case GSS_S_NO_CONTEXT:
251 			errStr = dgettext(TEXT_DOMAIN,
252 					"No context has been established");
253 			break;
254 
255 		case GSS_S_DEFECTIVE_TOKEN:
256 			errStr = dgettext(TEXT_DOMAIN,
257 					"Invalid token was supplied");
258 			break;
259 
260 		case GSS_S_DEFECTIVE_CREDENTIAL:
261 			errStr = dgettext(TEXT_DOMAIN,
262 					"Invalid credential was supplied");
263 			break;
264 
265 		case GSS_S_CREDENTIALS_EXPIRED:
266 			errStr = dgettext(TEXT_DOMAIN,
267 					"The referenced credential has"
268 					" expired");
269 			break;
270 
271 		case GSS_S_CONTEXT_EXPIRED:
272 			errStr = dgettext(TEXT_DOMAIN,
273 					"The referenced context has expired");
274 			break;
275 
276 		case GSS_S_FAILURE:
277 			errStr = dgettext(TEXT_DOMAIN,
278 					"Unspecified GSS failure.  Minor code"
279 					" may provide more information");
280 			break;
281 
282 		case GSS_S_BAD_QOP:
283 			errStr = dgettext(TEXT_DOMAIN,
284 					"The quality-of-protection (QOP) "
285 					"requested could not be provided");
286 			break;
287 
288 		case GSS_S_UNAUTHORIZED:
289 			errStr = dgettext(TEXT_DOMAIN,
290 					"The operation is forbidden by local"
291 					" security policy");
292 			break;
293 
294 		case GSS_S_UNAVAILABLE:
295 			errStr = dgettext(TEXT_DOMAIN,
296 					"The operation or option is not"
297 					" available or unsupported");
298 			break;
299 
300 		case GSS_S_DUPLICATE_ELEMENT:
301 			errStr = dgettext(TEXT_DOMAIN,
302 					"The requested credential element"
303 					" already exists");
304 			break;
305 
306 		case GSS_S_NAME_NOT_MN:
307 			errStr = dgettext(TEXT_DOMAIN,
308 					"The provided name was not mechanism"
309 					" specific (MN)");
310 			break;
311 
312 		case GSS_S_BAD_STATUS:
313 		default:
314 			errStr = dgettext(TEXT_DOMAIN,
315 					"An invalid status code was supplied");
316 		}
317 
318 		/* we must determine if the caller should call us again */
319 		if ((oneVal = GSS_SUPPLEMENTARY_INFO(status)) != 0)
320 			*msgCtxt = (OM_uint32)(oneVal << 1);
321 		else
322 			*msgCtxt = 0;
323 
324 	} else if ((*msgCtxt == 0 || *msgCtxt >= 2) &&
325 		(oneVal = GSS_SUPPLEMENTARY_INFO(status))) {
326 		/*
327 		 * if msgCtxt is not 0, then it should encode
328 		 * the supplementary error code we should be printing
329 		 */
330 		if (*msgCtxt >= 2)
331 			oneVal = (OM_uint32) (*msgCtxt) >> 1;
332 		else
333 			oneVal = GSS_SUPPLEMENTARY_INFO(status);
334 
335 		/* we display the errors LSB first */
336 		for (i = 0; i < 16; i++) {
337 			if (oneVal & mask) {
338 				haveErr = 1;
339 				break;
340 			}
341 			mask <<= 1;
342 		}
343 
344 		/* isolate the bit or if not found set to illegal value */
345 		if (haveErr)
346 			currErr = oneVal & mask;
347 		else
348 			currErr = 1 << 17; /* illegal value */
349 
350 		switch (currErr) {
351 		case GSS_S_CONTINUE_NEEDED:
352 			errStr = dgettext(TEXT_DOMAIN,
353 					"The routine must be called again to"
354 					" complete its function");
355 			break;
356 
357 		case GSS_S_DUPLICATE_TOKEN:
358 			errStr = dgettext(TEXT_DOMAIN,
359 					"The token was a duplicate of an"
360 					" earlier token");
361 			break;
362 
363 		case GSS_S_OLD_TOKEN:
364 			errStr = dgettext(TEXT_DOMAIN,
365 					"The token's validity period"
366 					" has expired");
367 			break;
368 
369 		case GSS_S_UNSEQ_TOKEN:
370 			errStr = dgettext(TEXT_DOMAIN,
371 					"A later token has already been"
372 					" processed");
373 			break;
374 
375 		case GSS_S_GAP_TOKEN:
376 			errStr = dgettext(TEXT_DOMAIN,
377 					"An expected per-message token was"
378 					" not received");
379 			break;
380 
381 		default:
382 			errStr = dgettext(TEXT_DOMAIN,
383 					"An invalid status code was supplied");
384 		}
385 
386 		/*
387 		 * we must check if there is any other supplementary errors
388 		 * if found, then turn off current bit, and store next value
389 		 * in msgCtxt shifted by 1 bit
390 		 */
391 		if (!haveErr)
392 			*msgCtxt = 0;
393 		else if (GSS_SUPPLEMENTARY_INFO(oneVal) ^ mask)
394 			*msgCtxt = (OM_uint32)
395 				((GSS_SUPPLEMENTARY_INFO(oneVal) ^ mask) << 1);
396 		else
397 			*msgCtxt = 0;
398 	}
399 
400 	if (errStr == NULL)
401 		errStr = dgettext(TEXT_DOMAIN,
402 				"An invalid status code was supplied");
403 
404 	/* now copy the status code and return to caller */
405 	outStr->length = strlen(errStr);
406 	outStr->value = strdup(errStr);
407 	if (outStr->value == NULL) {
408 		outStr->length = 0;
409 		return (GSS_S_FAILURE);
410 	}
411 
412 	return (GSS_S_COMPLETE);
413 } /* displayMajor */
414