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 /*
9  * lib/krb5/krb/recvauth.c
10  *
11  * Copyright 1991 by the Massachusetts Institute of Technology.
12  * All Rights Reserved.
13  *
14  * Export of this software from the United States of America may
15  *   require a specific license from the United States Government.
16  *   It is the responsibility of any person or organization contemplating
17  *   export to obtain such a license before exporting.
18  *
19  * WITHIN THAT CONSTRAINT, permission to use, copy, modify, and
20  * distribute this software and its documentation for any purpose and
21  * without fee is hereby granted, provided that the above copyright
22  * notice appear in all copies and that both that copyright notice and
23  * this permission notice appear in supporting documentation, and that
24  * the name of M.I.T. not be used in advertising or publicity pertaining
25  * to distribution of the software without specific, written prior
26  * permission.  Furthermore if you modify this software you must label
27  * your software as modified software and not distribute it in such a
28  * fashion that it might be confused with the original M.I.T. software.
29  * M.I.T. makes no representations about the suitability of
30  * this software for any purpose.  It is provided "as is" without express
31  * or implied warranty.
32  *
33  *
34  * convenience sendauth/recvauth functions
35  */
36 
37 #define NEED_SOCKETS
38 #include <k5-int.h>
39 #include <auth_con.h>
40 #include <com_err.h>
41 #include <errno.h>
42 #include <stdio.h>
43 #include <string.h>
44 
45 static const char sendauth_version[] = "KRB5_SENDAUTH_V1.0";
46 
47 static krb5_error_code
48 recvauth_common(krb5_context context,
49 		krb5_auth_context * auth_context,
50 		/* IN */
51 		krb5_pointer fd,
52 		char *appl_version,
53 		krb5_principal server,
54 		krb5_int32 flags,
55 		krb5_keytab keytab,
56 		/* OUT */
57 		krb5_ticket ** ticket,
58 		krb5_data *version)
59 {
60     krb5_auth_context	  new_auth_context;
61     krb5_flags		  ap_option;
62     krb5_error_code	  retval, problem;
63     krb5_data		  inbuf;
64     krb5_data		  outbuf;
65     krb5_rcache 	  rcache = 0;
66     krb5_octet		  response;
67     krb5_data		  null_server;
68     int                   need_error_free = 0;
69     int			  local_rcache = 0, local_authcon = 0;
70 
71 	/*
72 	 * Zero out problem variable.  If problem is set at the end of
73 	 * the intial version negotiation section, it means that we
74 	 * need to send an error code back to the client application
75 	 * and exit.
76 	 */
77 	problem = 0;
78 
79 	if (!(flags & KRB5_RECVAUTH_SKIP_VERSION)) {
80 	    /*
81 	     * First read the sendauth version string and check it.
82 	     */
83 	    if ((retval = krb5_read_message(context, fd, &inbuf)))
84 		return(retval);
85 	    if (strcmp(inbuf.data, sendauth_version)) {
86 		problem = KRB5_SENDAUTH_BADAUTHVERS;
87 	    }
88 	    krb5_xfree(inbuf.data);
89 	}
90 	if (flags & KRB5_RECVAUTH_BADAUTHVERS)
91 	    problem = KRB5_SENDAUTH_BADAUTHVERS;
92 
93 	/*
94 	 * Do the same thing for the application version string.
95 	 */
96 	if ((retval = krb5_read_message(context, fd, &inbuf)))
97 		return(retval);
98 	if (appl_version && strcmp(inbuf.data, appl_version)) {
99 		if (!problem)
100 			problem = KRB5_SENDAUTH_BADAPPLVERS;
101 	}
102 	if (version && !problem)
103 	    *version = inbuf;
104 	else
105 	    krb5_xfree(inbuf.data);
106 	/*
107 	 * OK, now check the problem variable.  If it's zero, we're
108 	 * fine and we can continue.  Otherwise, we have to signal an
109 	 * error to the client side and bail out.
110 	 */
111 	switch (problem) {
112 	case 0:
113 		response = 0;
114 		break;
115 	case KRB5_SENDAUTH_BADAUTHVERS:
116 		response = 1;
117 		break;
118 	case KRB5_SENDAUTH_BADAPPLVERS:
119 		response = 2;
120 		break;
121 	default:
122 		/*
123 		 * Should never happen!
124 		 */
125 		response = 255;
126 #ifdef SENDAUTH_DEBUG
127 		fprintf(stderr, "Programming botch in recvauth!  problem = %d",
128 			problem);
129 		abort();
130 #endif
131 		break;
132 	}
133 	/*
134 	 * Now we actually write the response.  If the response is non-zero,
135 	 * exit with a return value of problem
136 	 */
137 	if ((krb5_net_write(context, *((int *)fd), (char *)&response, 1)) < 0) {
138 		return(problem); /* We'll return the top-level problem */
139 	}
140 	if (problem)
141 	    return(problem);
142 
143     /* We are clear of errors here */
144 
145     /*
146      * Now, let's read the AP_REQ message and decode it
147      */
148     if ((retval = krb5_read_message(context, fd, &inbuf)))
149         return retval;
150 
151     if (*auth_context == NULL) {
152 	problem = krb5_auth_con_init(context, &new_auth_context);
153 	*auth_context = new_auth_context;
154 	local_authcon = 1;
155     }
156     krb5_auth_con_getrcache(context, *auth_context, &rcache);
157     if ((!problem) && rcache == NULL) {
158         /*
159          * Setup the replay cache.
160          */
161         if (server) {
162             problem = krb5_get_server_rcache(context,
163 			krb5_princ_component(context, server, 0), &rcache);
164         } else {
165     	    null_server.length = 7;
166     	    null_server.data = "default";
167     	    problem = krb5_get_server_rcache(context, &null_server, &rcache);
168         }
169         if (!problem)
170 	    problem = krb5_auth_con_setrcache(context, *auth_context, rcache);
171 	local_rcache = 1;
172     }
173     if (!problem) {
174 	problem = krb5_rd_req(context, auth_context, &inbuf, server,
175 			      keytab, &ap_option, ticket);
176 	krb5_xfree(inbuf.data);
177     }
178 
179     /*
180      * If there was a problem, send back a krb5_error message,
181      * preceeded by the length of the krb5_error message.  If
182      * everything's ok, send back 0 for the length.
183      */
184     if (problem) {
185 	krb5_error	error;
186 	const	char *message;
187 
188 	memset((char *)&error, 0, sizeof(error));
189 	krb5_us_timeofday(context, &error.stime, &error.susec);
190 	if(server)
191 		error.server = server;
192 	else {
193 		/* If this fails - ie. ENOMEM we are hosed
194 		   we cannot even send the error if we wanted to... */
195 		(void) krb5_parse_name(context, "????", &error.server);
196 		need_error_free = 1;
197 	}
198 
199 	error.error = problem - ERROR_TABLE_BASE_krb5;
200 	if (error.error > 127)
201 		error.error = KRB_ERR_GENERIC;
202 	message = error_message(problem);
203 	error.text.length  = strlen(message) + 1;
204 	if (!(error.text.data = malloc(error.text.length))) {
205 	    retval = ENOMEM;
206 	    goto cleanup;
207 	}
208 	strcpy(error.text.data, message);
209 	if ((retval = krb5_mk_error(context, &error, &outbuf)) != 0) {
210 	    free(error.text.data);
211 	    goto cleanup;
212 	}
213 	free(error.text.data);
214 	if(need_error_free)
215 		krb5_free_principal(context, error.server);
216 
217     } else {
218 	outbuf.length = 0;
219 	outbuf.data = 0;
220     }
221 
222     retval = krb5_write_message(context, fd, &outbuf);
223     if (outbuf.data) {
224 	krb5_xfree(outbuf.data);
225     	/* We sent back an error, we need cleanup then return */
226     	retval = problem;
227     	goto cleanup;
228     }
229     if (retval)
230 	goto cleanup;
231 
232     /* Here lies the mutual authentication stuff... */
233     if ((ap_option & AP_OPTS_MUTUAL_REQUIRED)) {
234 	if ((retval = krb5_mk_rep(context, *auth_context, &outbuf))) {
235 	    return(retval);
236 	}
237 	retval = krb5_write_message(context, fd, &outbuf);
238 	krb5_xfree(outbuf.data);
239     }
240 
241 cleanup:;
242     if (retval) {
243 	if (local_authcon) {
244 	    krb5_auth_con_free(context, *auth_context);
245 	} else if (local_rcache && rcache != NULL) {
246 	    (void) krb5_rc_close(context, rcache);
247 	    krb5_auth_con_setrcache(context, *auth_context, NULL);
248 	}
249     }
250     return retval;
251 }
252 
253 krb5_error_code KRB5_CALLCONV
254 krb5_recvauth(krb5_context context, krb5_auth_context *auth_context, krb5_pointer fd, char *appl_version, krb5_principal server, krb5_int32 flags, krb5_keytab keytab, krb5_ticket **ticket)
255 {
256     return recvauth_common(context, auth_context, fd, appl_version,
257 			    server, flags, keytab, ticket, 0);
258 }
259 
260 krb5_error_code KRB5_CALLCONV
261 krb5_recvauth_version(krb5_context context,
262 		      krb5_auth_context *auth_context,
263 		      /* IN */
264 		      krb5_pointer fd,
265 		      krb5_principal server,
266 		      krb5_int32 flags,
267 		      krb5_keytab keytab,
268 		      /* OUT */
269 		      krb5_ticket **ticket,
270 		      krb5_data *version)
271 {
272     return recvauth_common (context, auth_context, fd, 0,
273 			    server, flags, keytab, ticket, version);
274 }
275