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 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 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 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