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