1 /*
2 * Copyright 2009 Sun Microsystems, Inc. All rights reserved.
3 * Use is subject to license terms.
4 */
5
6
7 /*
8 * lib/krb5/krb/recvauth.c
9 *
10 * Copyright 1991 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 * convenience sendauth/recvauth functions
34 */
35
36 #include "k5-int.h"
37 #include "auth_con.h"
38 #include "com_err.h"
39 #include <errno.h>
40 #include <stdio.h>
41 #include <string.h>
42
43 static const char sendauth_version[] = "KRB5_SENDAUTH_V1.0";
44
45 static krb5_error_code
recvauth_common(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,krb5_data * version)46 recvauth_common(krb5_context context,
47 krb5_auth_context * auth_context,
48 /* IN */
49 krb5_pointer fd,
50 char *appl_version,
51 krb5_principal server,
52 krb5_int32 flags,
53 krb5_keytab keytab,
54 /* OUT */
55 krb5_ticket ** ticket,
56 krb5_data *version)
57 {
58 krb5_auth_context new_auth_context;
59 krb5_flags ap_option;
60 krb5_error_code retval, problem;
61 krb5_data inbuf;
62 krb5_data outbuf;
63 krb5_rcache rcache = 0;
64 krb5_octet response;
65 krb5_data null_server;
66 int need_error_free = 0;
67 int local_rcache = 0, local_authcon = 0;
68
69 /*
70 * Zero out problem variable. If problem is set at the end of
71 * the intial version negotiation section, it means that we
72 * need to send an error code back to the client application
73 * and exit.
74 */
75 problem = 0;
76
77 if (!(flags & KRB5_RECVAUTH_SKIP_VERSION)) {
78 /*
79 * First read the sendauth version string and check it.
80 */
81 if ((retval = krb5_read_message(context, fd, &inbuf)))
82 return(retval);
83 if (strcmp(inbuf.data, sendauth_version)) {
84 problem = KRB5_SENDAUTH_BADAUTHVERS;
85 }
86 krb5_xfree(inbuf.data);
87 }
88 if (flags & KRB5_RECVAUTH_BADAUTHVERS)
89 problem = KRB5_SENDAUTH_BADAUTHVERS;
90
91 /*
92 * Do the same thing for the application version string.
93 */
94 if ((retval = krb5_read_message(context, fd, &inbuf)))
95 return(retval);
96 if (appl_version && strcmp(inbuf.data, appl_version)) {
97 if (!problem)
98 problem = KRB5_SENDAUTH_BADAPPLVERS;
99 }
100 if (version && !problem)
101 *version = inbuf;
102 else
103 krb5_xfree(inbuf.data);
104 /*
105 * OK, now check the problem variable. If it's zero, we're
106 * fine and we can continue. Otherwise, we have to signal an
107 * error to the client side and bail out.
108 */
109 switch (problem) {
110 case 0:
111 response = 0;
112 break;
113 case KRB5_SENDAUTH_BADAUTHVERS:
114 response = 1;
115 break;
116 case KRB5_SENDAUTH_BADAPPLVERS:
117 response = 2;
118 break;
119 default:
120 /*
121 * Should never happen!
122 */
123 response = 255;
124 #ifdef SENDAUTH_DEBUG
125 fprintf(stderr, "Programming botch in recvauth! problem = %d",
126 problem);
127 abort();
128 #endif
129 break;
130 }
131 /*
132 * Now we actually write the response. If the response is non-zero,
133 * exit with a return value of problem
134 */
135 if ((krb5_net_write(context, *((int *)fd), (char *)&response, 1)) < 0) {
136 return(problem); /* We'll return the top-level problem */
137 }
138 if (problem)
139 return(problem);
140
141 /* We are clear of errors here */
142
143 /*
144 * Now, let's read the AP_REQ message and decode it
145 */
146 if ((retval = krb5_read_message(context, fd, &inbuf)))
147 return retval;
148
149 if (*auth_context == NULL) {
150 problem = krb5_auth_con_init(context, &new_auth_context);
151 *auth_context = new_auth_context;
152 local_authcon = 1;
153 }
154 krb5_auth_con_getrcache(context, *auth_context, &rcache);
155 if ((!problem) && rcache == NULL) {
156 /*
157 * Setup the replay cache.
158 */
159 if (server) {
160 problem = krb5_get_server_rcache(context,
161 krb5_princ_component(context, server, 0), &rcache);
162 } else {
163 null_server.length = 7;
164 null_server.data = "default";
165 problem = krb5_get_server_rcache(context, &null_server, &rcache);
166 }
167 if (!problem)
168 problem = krb5_auth_con_setrcache(context, *auth_context, rcache);
169 local_rcache = 1;
170 }
171 if (!problem) {
172 problem = krb5_rd_req(context, auth_context, &inbuf, server,
173 keytab, &ap_option, ticket);
174 krb5_xfree(inbuf.data);
175 }
176
177 /*
178 * If there was a problem, send back a krb5_error message,
179 * preceeded by the length of the krb5_error message. If
180 * everything's ok, send back 0 for the length.
181 */
182 if (problem) {
183 krb5_error error;
184 const char *message;
185
186 memset((char *)&error, 0, sizeof(error));
187 krb5_us_timeofday(context, &error.stime, &error.susec);
188 if(server)
189 error.server = server;
190 else {
191 /* If this fails - ie. ENOMEM we are hosed
192 we cannot even send the error if we wanted to... */
193 (void) krb5_parse_name(context, "????", &error.server);
194 need_error_free = 1;
195 }
196
197 error.error = problem - ERROR_TABLE_BASE_krb5;
198 if (error.error > 127)
199 error.error = KRB_ERR_GENERIC;
200 message = error_message(problem);
201 error.text.length = strlen(message) + 1;
202 if (!(error.text.data = malloc(error.text.length))) {
203 retval = ENOMEM;
204 goto cleanup;
205 }
206 strcpy(error.text.data, message);
207 /* Solaris Kerberos */
208 if ((retval = krb5_mk_error(context, &error, &outbuf)) != 0) {
209 free(error.text.data);
210 goto cleanup;
211 }
212 free(error.text.data);
213 if(need_error_free)
214 krb5_free_principal(context, error.server);
215
216 } else {
217 outbuf.length = 0;
218 outbuf.data = 0;
219 }
220
221 retval = krb5_write_message(context, fd, &outbuf);
222 if (outbuf.data) {
223 krb5_xfree(outbuf.data);
224 /* We sent back an error, we need cleanup then return */
225 retval = problem;
226 goto cleanup;
227 }
228 if (retval)
229 goto cleanup;
230
231 /* Here lies the mutual authentication stuff... */
232 if ((ap_option & AP_OPTS_MUTUAL_REQUIRED)) {
233 if ((retval = krb5_mk_rep(context, *auth_context, &outbuf))) {
234 return(retval);
235 }
236 retval = krb5_write_message(context, fd, &outbuf);
237 krb5_xfree(outbuf.data);
238 }
239
240 cleanup:;
241 if (retval) {
242 if (local_authcon) {
243 krb5_auth_con_free(context, *auth_context);
244 /* Solaris Kerberos */
245 *auth_context = NULL;
246 } else if (local_rcache && rcache != NULL) {
247 /* Solaris Kerberos */
248 (void) krb5_rc_close(context, rcache);
249 krb5_auth_con_setrcache(context, *auth_context, NULL);
250 }
251 }
252 return retval;
253 }
254
255 krb5_error_code KRB5_CALLCONV
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)256 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)
257 {
258 return recvauth_common (context, auth_context, fd, appl_version,
259 server, flags, keytab, ticket, 0);
260 }
261
262 krb5_error_code KRB5_CALLCONV
krb5_recvauth_version(krb5_context context,krb5_auth_context * auth_context,krb5_pointer fd,krb5_principal server,krb5_int32 flags,krb5_keytab keytab,krb5_ticket ** ticket,krb5_data * version)263 krb5_recvauth_version(krb5_context context,
264 krb5_auth_context *auth_context,
265 /* IN */
266 krb5_pointer fd,
267 krb5_principal server,
268 krb5_int32 flags,
269 krb5_keytab keytab,
270 /* OUT */
271 krb5_ticket **ticket,
272 krb5_data *version)
273 {
274 return recvauth_common (context, auth_context, fd, 0,
275 server, flags, keytab, ticket, version);
276 }
277