/* * Copyright 2009 Sun Microsystems, Inc. All rights reserved. * Use is subject to license terms. */ /* * lib/krb5/krb/recvauth.c * * Copyright 1991 by the Massachusetts Institute of Technology. * All Rights Reserved. * * Export of this software from the United States of America may * require a specific license from the United States Government. * It is the responsibility of any person or organization contemplating * export to obtain such a license before exporting. * * WITHIN THAT CONSTRAINT, permission to use, copy, modify, and * distribute this software and its documentation for any purpose and * without fee is hereby granted, provided that the above copyright * notice appear in all copies and that both that copyright notice and * this permission notice appear in supporting documentation, and that * the name of M.I.T. not be used in advertising or publicity pertaining * to distribution of the software without specific, written prior * permission. Furthermore if you modify this software you must label * your software as modified software and not distribute it in such a * fashion that it might be confused with the original M.I.T. software. * M.I.T. makes no representations about the suitability of * this software for any purpose. It is provided "as is" without express * or implied warranty. * * * convenience sendauth/recvauth functions */ #include "k5-int.h" #include "auth_con.h" #include "com_err.h" #include #include #include static const char sendauth_version[] = "KRB5_SENDAUTH_V1.0"; static krb5_error_code recvauth_common(krb5_context context, krb5_auth_context * auth_context, /* IN */ krb5_pointer fd, char *appl_version, krb5_principal server, krb5_int32 flags, krb5_keytab keytab, /* OUT */ krb5_ticket ** ticket, krb5_data *version) { krb5_auth_context new_auth_context; krb5_flags ap_option; krb5_error_code retval, problem; krb5_data inbuf; krb5_data outbuf; krb5_rcache rcache = 0; krb5_octet response; krb5_data null_server; int need_error_free = 0; int local_rcache = 0, local_authcon = 0; /* * Zero out problem variable. If problem is set at the end of * the intial version negotiation section, it means that we * need to send an error code back to the client application * and exit. */ problem = 0; if (!(flags & KRB5_RECVAUTH_SKIP_VERSION)) { /* * First read the sendauth version string and check it. */ if ((retval = krb5_read_message(context, fd, &inbuf))) return(retval); if (strcmp(inbuf.data, sendauth_version)) { problem = KRB5_SENDAUTH_BADAUTHVERS; } krb5_xfree(inbuf.data); } if (flags & KRB5_RECVAUTH_BADAUTHVERS) problem = KRB5_SENDAUTH_BADAUTHVERS; /* * Do the same thing for the application version string. */ if ((retval = krb5_read_message(context, fd, &inbuf))) return(retval); if (appl_version && strcmp(inbuf.data, appl_version)) { if (!problem) problem = KRB5_SENDAUTH_BADAPPLVERS; } if (version && !problem) *version = inbuf; else krb5_xfree(inbuf.data); /* * OK, now check the problem variable. If it's zero, we're * fine and we can continue. Otherwise, we have to signal an * error to the client side and bail out. */ switch (problem) { case 0: response = 0; break; case KRB5_SENDAUTH_BADAUTHVERS: response = 1; break; case KRB5_SENDAUTH_BADAPPLVERS: response = 2; break; default: /* * Should never happen! */ response = 255; #ifdef SENDAUTH_DEBUG fprintf(stderr, "Programming botch in recvauth! problem = %d", problem); abort(); #endif break; } /* * Now we actually write the response. If the response is non-zero, * exit with a return value of problem */ if ((krb5_net_write(context, *((int *)fd), (char *)&response, 1)) < 0) { return(problem); /* We'll return the top-level problem */ } if (problem) return(problem); /* We are clear of errors here */ /* * Now, let's read the AP_REQ message and decode it */ if ((retval = krb5_read_message(context, fd, &inbuf))) return retval; if (*auth_context == NULL) { problem = krb5_auth_con_init(context, &new_auth_context); *auth_context = new_auth_context; local_authcon = 1; } krb5_auth_con_getrcache(context, *auth_context, &rcache); if ((!problem) && rcache == NULL) { /* * Setup the replay cache. */ if (server) { problem = krb5_get_server_rcache(context, krb5_princ_component(context, server, 0), &rcache); } else { null_server.length = 7; null_server.data = "default"; problem = krb5_get_server_rcache(context, &null_server, &rcache); } if (!problem) problem = krb5_auth_con_setrcache(context, *auth_context, rcache); local_rcache = 1; } if (!problem) { problem = krb5_rd_req(context, auth_context, &inbuf, server, keytab, &ap_option, ticket); krb5_xfree(inbuf.data); } /* * If there was a problem, send back a krb5_error message, * preceeded by the length of the krb5_error message. If * everything's ok, send back 0 for the length. */ if (problem) { krb5_error error; const char *message; memset((char *)&error, 0, sizeof(error)); krb5_us_timeofday(context, &error.stime, &error.susec); if(server) error.server = server; else { /* If this fails - ie. ENOMEM we are hosed we cannot even send the error if we wanted to... */ (void) krb5_parse_name(context, "????", &error.server); need_error_free = 1; } error.error = problem - ERROR_TABLE_BASE_krb5; if (error.error > 127) error.error = KRB_ERR_GENERIC; message = error_message(problem); error.text.length = strlen(message) + 1; if (!(error.text.data = malloc(error.text.length))) { retval = ENOMEM; goto cleanup; } strcpy(error.text.data, message); /* Solaris Kerberos */ if ((retval = krb5_mk_error(context, &error, &outbuf)) != 0) { free(error.text.data); goto cleanup; } free(error.text.data); if(need_error_free) krb5_free_principal(context, error.server); } else { outbuf.length = 0; outbuf.data = 0; } retval = krb5_write_message(context, fd, &outbuf); if (outbuf.data) { krb5_xfree(outbuf.data); /* We sent back an error, we need cleanup then return */ retval = problem; goto cleanup; } if (retval) goto cleanup; /* Here lies the mutual authentication stuff... */ if ((ap_option & AP_OPTS_MUTUAL_REQUIRED)) { if ((retval = krb5_mk_rep(context, *auth_context, &outbuf))) { return(retval); } retval = krb5_write_message(context, fd, &outbuf); krb5_xfree(outbuf.data); } cleanup:; if (retval) { if (local_authcon) { krb5_auth_con_free(context, *auth_context); /* Solaris Kerberos */ *auth_context = NULL; } else if (local_rcache && rcache != NULL) { /* Solaris Kerberos */ (void) krb5_rc_close(context, rcache); krb5_auth_con_setrcache(context, *auth_context, NULL); } } return retval; } 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) { return recvauth_common (context, auth_context, fd, appl_version, server, flags, keytab, ticket, 0); } krb5_error_code KRB5_CALLCONV krb5_recvauth_version(krb5_context context, krb5_auth_context *auth_context, /* IN */ krb5_pointer fd, krb5_principal server, krb5_int32 flags, krb5_keytab keytab, /* OUT */ krb5_ticket **ticket, krb5_data *version) { return recvauth_common (context, auth_context, fd, 0, server, flags, keytab, ticket, version); }