/* * Copyright © 2007 Moritz Bechler * * * This program is free software; you can redistribute it and/or modify * it under the terms of the GNU General Public License as published by * * the Free Software Foundation; either version 3 of the License, or * (at your option) any later version. * * This program is distributed in the hope that it will be useful, * but WITHOUT ANY WARRANTY; without even the implied warranty of * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the * GNU General Public License for more details. * * You should have received a copy of the GNU General Public License * along with this program. If not, see . */ #include "modp_b64.h" #include #include #include #include #include #define BUFFER_SIZE 10240 char *program_name = NULL; char *server_principal = NULL; int debug_enabled = 0; gss_cred_id_t server_creds = GSS_C_NO_CREDENTIAL; void usage() { fprintf(stderr, "Usage: %s [-d] \n" " : path to the keytab which contains \n" " : GSS principal to use (must be HTTP@F.Q.D.N)\n" " -d enable debugging.\n" " -h this message\n\n", program_name); } static void gssapi_handle_error(OM_uint32 major, OM_uint32 minor) { OM_uint32 error_context = 0; OM_uint32 minor_status = 0; gss_buffer_desc error_buffer; gss_display_status (&minor_status, major, GSS_C_GSS_CODE, GSS_C_NO_OID, &error_context, &error_buffer); while(error_context) { fprintf(stderr, "%s\n", error_buffer.value); gss_release_buffer(&minor_status, &error_buffer); gss_display_status (&minor_status, major, GSS_C_GSS_CODE, GSS_C_NO_OID, &error_context, &error_buffer); } fprintf(stderr, "%s\n", error_buffer.value); gss_release_buffer(&minor_status, &error_buffer); if(minor) { fprintf(stderr, "GSSAPI mechanism error #%d:\n", minor); gss_display_status (&minor_status, minor, GSS_C_MECH_CODE, GSS_C_NO_OID, &error_context, &error_buffer); while(error_context) { fprintf(stderr, "%s\n", error_buffer.value); gss_release_buffer(&minor_status, &error_buffer); gss_display_status (&minor_status, minor_status, GSS_C_MECH_CODE, GSS_C_NO_OID, &error_context, &error_buffer); } fprintf(stderr, "%s\n", error_buffer.value); gss_release_buffer(&minor_status, &error_buffer); } } void acquire_server_creds(void) { OM_uint32 status = 0, minor_status = 0; if(debug_enabled) fprintf(stderr, "Obtainting/renewing server credentials\n"); if(server_creds != GSS_C_NO_CREDENTIAL) { gss_release_cred(&minor_status, &server_creds); } gss_name_t server_name = GSS_C_NO_NAME; gss_buffer_desc nametmp; nametmp.value = server_principal; nametmp.length = strlen(server_principal); status = gss_import_name(&minor_status, &nametmp, GSS_C_NT_HOSTBASED_SERVICE, &server_name); if(GSS_ERROR(status)) { fprintf(stderr, "While parsing name:\n"); gssapi_handle_error(status, minor_status); exit(-1); } status = gss_acquire_cred(&minor_status, server_name, 0, GSS_C_NO_OID_SET, GSS_C_ACCEPT, &server_creds, NULL, NULL); if(GSS_ERROR(status)) { fprintf(stderr, "While obtaining server credentials:\n"); gssapi_handle_error(status, minor_status); exit(-1); } gss_release_name(&minor_status, &server_name); } int manage_request() { char buf[BUFFER_SIZE]; char *c; int oversized = 0; OM_uint32 status = 0, minor_status = 0; try_again: if (fgets(buf, BUFFER_SIZE, stdin) == NULL) return 0; c = memchr(buf, '\n', BUFFER_SIZE); /* safer against overrun than strchr */ if (c) { if (oversized) { fprintf(stdout, "BH illegal request\n"); fprintf(stderr, "Illegal request received: '%s'\n", buf); return 1; } *c = '\0'; } else { fprintf(stderr, "No newline in '%s'\n", buf); oversized = 1; goto try_again; } if(debug_enabled) fprintf(stderr, "Got '%s' from Squid\n", buf); // verify crendtial validity OM_uint32 lifetime_remain = 0; if(gss_inquire_cred(&minor_status, server_creds, NULL, &lifetime_remain, NULL, NULL) != GSS_S_COMPLETE) { acquire_server_creds(); } if(lifetime_remain != GSS_C_INDEFINITE && lifetime_remain < 10) { acquire_server_creds(); } if(memcmp(buf, "YR ", 3) == 0) { /* authentication request */ gss_buffer_desc input; gss_buffer_desc output; memset(&output, 0, sizeof(output)); gss_name_t username = GSS_C_NO_NAME; gss_buffer_desc nametmp; gss_ctx_id_t gss_context = GSS_C_NO_CONTEXT; input.value = malloc(modp_b64_decode_len(strlen(buf+3))); input.length = modp_b64_decode(input.value, buf+3, strlen(buf+3)); if(input.length == -1) { fprintf(stdout, "BH Failed to decode base64 request\n"); free(input.value); return 1; } if(debug_enabled) fprintf(stderr, "Token length: %d\n", input.length); status = gss_accept_sec_context( &minor_status, &gss_context, server_creds, &input, GSS_C_NO_CHANNEL_BINDINGS, &username, NULL, &output, NULL, NULL, NULL); free(input.value); gss_buffer_t tmpbuf = GSS_C_NO_BUFFER; if(GSS_ERROR(status) && status != GSS_S_CONTINUE_NEEDED) { gssapi_handle_error(status, minor_status); gss_release_buffer(&minor_status, &output); gss_delete_sec_context(&minor_status, gss_context, tmpbuf); gss_release_buffer(&minor_status, tmpbuf); gss_release_name(&minor_status, &username); fprintf(stdout, "NA Negotiate authentication failed\n"); return 1; } gss_delete_sec_context(&minor_status, gss_context, tmpbuf); gss_release_buffer(&minor_status, tmpbuf); if(status == GSS_S_CONTINUE_NEEDED) { char *encoded = malloc(modp_b64_encode_len(output.length)); if(!modp_b64_encode(encoded, output.value, output.length)) { fprintf(stdout, "BH Failed to do base64 encoding of token\n"); gss_release_buffer(&minor_status, &output); free(encoded); return 1; } gss_release_buffer(&minor_status, &output); if(debug_enabled) fprintf(stderr, "Continuation required, sending: %s\n", encoded); fprintf(stdout, "TT * %s\n", encoded); free(encoded); return 1; } gss_display_name(&minor_status, username, &nametmp, NULL); gss_release_name(&minor_status, &username); if(debug_enabled) fprintf(stderr, "Authenticated as %s\n", nametmp.value); if(output.length != 0) { char *encoded = malloc(modp_b64_encode_len(output.length)); if(!modp_b64_encode(encoded, output.value, output.length)) { fprintf(stdout, "BH Failed to do base64 encoding of token\n"); gss_release_buffer(&minor_status, &output); gss_release_buffer(&minor_status, &nametmp); free(encoded); return 1; } gss_release_buffer(&minor_status, &output); fprintf(stdout, "AF %s %s\n", encoded, nametmp.value); free(encoded); } else { if(debug_enabled) { fprintf(stderr, "Sending dummy response\n"); } fprintf(stdout, "AF * %s\n", nametmp.value); } gss_release_buffer(&minor_status, &nametmp); return 1; } else { fprintf(stdout, "BH Invalid message recieved\n"); return 1; } return 1; } int main(int argc, char *argv[]) { program_name = argv[0]; if(argc < 3 || argc > 4) { usage(); exit(0); } server_principal = argv[2]; if(argc == 4 && strcmp(argv[3], "-d") == 0) debug_enabled = 1; if(debug_enabled) { fprintf(stderr,"%s build " __DATE__ ", " __TIME__ " starting up...\n", program_name); fprintf(stderr,"Running as principal %s\n", server_principal); } /* initialize FDescs */ setbuf(stdout, NULL); setbuf(stderr, NULL); char *keytab = argv[1]; if(krb5_gss_register_acceptor_identity(keytab) != GSS_S_COMPLETE) { fprintf(stderr, "Failed to set keytab"); } acquire_server_creds(); while (manage_request()) { /* everything is done within manage_request */ } exit(0); }