/*extern PyObject *GssException_class; extern PyObject *KrbException_class; char* server_principal_details(const char* service, const char* hostname) { char match[1024]; int match_len = 0; char* result = NULL; int code; krb5_context kcontext; krb5_keytab kt = NULL; krb5_kt_cursor cursor = NULL; krb5_keytab_entry entry; char* pname = NULL; // Generate the principal prefix we want to match snprintf(match, 1024, "%s/%s@", service, hostname); match_len = strlen(match); code = krb5_init_context(&kcontext); if (code) { PyErr_SetObject(KrbException_class, Py_BuildValue("((s:i))", "Cannot initialize Kerberos5 context", code)); return NULL; } if ((code = krb5_kt_default(kcontext, &kt))) { PyErr_SetObject(KrbException_class, Py_BuildValue("((s:i))", "Cannot get default keytab", code)); goto end; } if ((code = krb5_kt_start_seq_get(kcontext, kt, &cursor))) { PyErr_SetObject(KrbException_class, Py_BuildValue("((s:i))", "Cannot get sequence cursor from keytab", code)); goto end; } while ((code = krb5_kt_next_entry(kcontext, kt, &entry, &cursor)) == 0) { if ((code = krb5_unparse_name(kcontext, entry.principal, &pname))) { PyErr_SetObject(KrbException_class, Py_BuildValue("((s:i))", "Cannot parse principal name from keytab", code)); goto end; } if (strncmp(pname, match, match_len) == 0) { result = malloc(strlen(pname) + 1); strcpy(result, pname); krb5_free_unparsed_name(kcontext, pname); krb5_free_keytab_entry_contents(kcontext, &entry); break; } krb5_free_unparsed_name(kcontext, pname); krb5_free_keytab_entry_contents(kcontext, &entry); } if (result == NULL) { PyErr_SetObject(KrbException_class, Py_BuildValue("((s:i))", "Principal not found in keytab", -1)); } end: if (cursor) krb5_kt_end_seq_get(kcontext, kt, &cursor); if (kt) krb5_kt_close(kcontext, kt); krb5_free_context(kcontext); return result; } */ gss_client_response *authenticate_gss_client_init(const char* service, long int gss_flags, gss_client_state* state) { OM_uint32 maj_stat; OM_uint32 min_stat; gss_buffer_desc name_token = GSS_C_EMPTY_BUFFER; gss_client_response *response = NULL; int ret = AUTH_GSS_COMPLETE; state->server_name = GSS_C_NO_NAME; state->context = GSS_C_NO_CONTEXT; state->gss_flags = gss_flags; state->username = NULL; state->response = NULL; // Import server name first name_token.length = strlen(service); name_token.value = (char *)service; maj_stat = gss_import_name(&min_stat, &name_token, gss_krb5_nt_service_name, &state->server_name); if (GSS_ERROR(maj_stat)) { response = gss_error(maj_stat, min_stat); response->return_code = AUTH_GSS_ERROR; goto end; } end: if(response == NULL) { response = calloc(1, sizeof(gss_client_response)); response->return_code = ret; } return response; }
gss_client_response *authenticate_gss_client_unwrap(gss_client_state *state, const char *challenge) { OM_uint32 maj_stat; OM_uint32 min_stat; gss_buffer_desc input_token = GSS_C_EMPTY_BUFFER; gss_buffer_desc output_token = GSS_C_EMPTY_BUFFER; gss_client_response *response = NULL; int ret = AUTH_GSS_CONTINUE; // Always clear out the old response if(state->response != NULL) { free(state->response); state->response = NULL; } // If there is a challenge (data from the server) we need to give it to GSS if(challenge && *challenge) { int len; input_token.value = base64_decode(challenge, &len); input_token.length = len; } // Do GSSAPI step maj_stat = gss_unwrap(&min_stat, state->context, &input_token, &output_token, NULL, NULL); if(maj_stat != GSS_S_COMPLETE) { response = gss_error(__func__, "gss_unwrap", maj_stat, min_stat); response->return_code = AUTH_GSS_ERROR; goto end; } else { ret = AUTH_GSS_COMPLETE; } // Grab the client response if(output_token.length) { state->response = base64_encode((const unsigned char *)output_token.value, output_token.length); gss_release_buffer(&min_stat, &output_token); } end: if(output_token.value) gss_release_buffer(&min_stat, &output_token); if(input_token.value) free(input_token.value); if(response == NULL) { response = calloc(1, sizeof(gss_client_response)); if(response == NULL) die1("Memory allocation failed"); response->return_code = ret; } // Return the response return response; }
static gss_client_response *store_gss_creds(gss_server_state *state) { OM_uint32 maj_stat, min_stat; krb5_principal princ = NULL; krb5_ccache ccache = NULL; krb5_error_code problem; krb5_context context; gss_client_response *response = NULL; problem = krb5_init_context(&context); if (problem) { response = other_error("No auth_data value in request from client"); return response; } problem = krb5_parse_name(context, state->username, &princ); if (problem) { response = krb5_ctx_error(context, problem); goto end; } if ((response = create_krb5_ccache(state, context, princ, &ccache))) { goto end; } maj_stat = gss_krb5_copy_ccache(&min_stat, state->client_creds, ccache); if (GSS_ERROR(maj_stat)) { response = gss_error(__func__, "gss_krb5_copy_ccache", maj_stat, min_stat); response->return_code = AUTH_GSS_ERROR; goto end; } krb5_cc_close(context, ccache); ccache = NULL; response = calloc(1, sizeof(gss_client_response)); if(response == NULL) die1("Memory allocation failed"); // TODO: something other than AUTH_GSS_COMPLETE? response->return_code = AUTH_GSS_COMPLETE; end: if (princ) krb5_free_principal(context, princ); if (ccache) krb5_cc_destroy(context, ccache); krb5_free_context(context); return response; }
static int k5_decrypt( void *cookie, void *buf, ssize_t buflen, void **decbuf, ssize_t *decbuflen) { struct tcp_conn *rc = cookie; gss_buffer_desc enctok; gss_buffer_desc dectok; OM_uint32 maj_stat, min_stat; int conf_state, qop_state; if (rc->conf_fn && rc->conf_fn("kencrypt", rc->datap)) { auth_debug(1, _("krb5: k5_decrypt: enter\n")); if (rc->auth == 1) { enctok.length = buflen; enctok.value = buf; auth_debug(1, _("krb5: k5_decrypt: decrypting %zu bytes\n"), enctok.length); assert(rc->gss_context != GSS_C_NO_CONTEXT); maj_stat = gss_unseal(&min_stat, rc->gss_context, &enctok, &dectok, &conf_state, &qop_state); if (maj_stat != (OM_uint32)GSS_S_COMPLETE) { auth_debug(1, _("krb5 decrypt error from %s: %s\n"), rc->hostname, gss_error(maj_stat, min_stat)); return (-1); } auth_debug(1, _("krb5: k5_decrypt: give %zu bytes\n"), dectok.length); *decbuf = dectok.value; *decbuflen = dectok.length; } else { *decbuf = buf; *decbuflen = buflen; } auth_debug(1, _("krb5: k5_decrypt: exit\n")); } else { *decbuf = buf; *decbuflen = buflen; } return (0); }
static int k5_encrypt( void *cookie, void *buf, ssize_t buflen, void **encbuf, ssize_t *encbuflen) { struct tcp_conn *rc = cookie; gss_buffer_desc dectok; gss_buffer_desc enctok; OM_uint32 maj_stat, min_stat; int conf_state; if (rc->conf_fn && rc->conf_fn("kencrypt", rc->datap)) { auth_debug(1, _("krb5: k5_encrypt: enter %p\n"), rc); dectok.length = buflen; dectok.value = buf; if (rc->auth == 1) { assert(rc->gss_context != GSS_C_NO_CONTEXT); maj_stat = gss_seal(&min_stat, rc->gss_context, 1, GSS_C_QOP_DEFAULT, &dectok, &conf_state, &enctok); if (maj_stat != (OM_uint32)GSS_S_COMPLETE || conf_state == 0) { auth_debug(1, _("krb5 encrypt error to %s: %s\n"), rc->hostname, gss_error(maj_stat, min_stat)); return (-1); } auth_debug(1, _("krb5: k5_encrypt: give %zu bytes\n"), enctok.length); *encbuf = enctok.value; *encbuflen = enctok.length; } else { *encbuf = buf; *encbuflen = buflen; } auth_debug(1, _("krb5: k5_encrypt: exit\n")); } return (0); }
static gss_client_response *init_gss_creds(const char *credential_cache, gss_cred_id_t *cred) { OM_uint32 maj_stat; OM_uint32 min_stat; krb5_context context; krb5_error_code problem; gss_client_response *response = NULL; krb5_ccache ccache = NULL; *cred = GSS_C_NO_CREDENTIAL; if (credential_cache == NULL || strlen(credential_cache) == 0) { return NULL; } problem = krb5_init_context(&context); if (problem) { return other_error("unable to initialize krb5 context (%d)", (int)problem); } problem = krb5_cc_resolve(context, credential_cache, &ccache); if (problem) { response = krb5_ctx_error(context, problem); goto done; } maj_stat = gss_krb5_import_cred(&min_stat, ccache, NULL, NULL, cred); if (GSS_ERROR(maj_stat)) { response = gss_error(__func__, "gss_krb5_import_cred", maj_stat, min_stat); response->return_code = AUTH_GSS_ERROR; } done: if (response && ccache) { krb5_cc_close(context, ccache); } krb5_free_context(context); return response; }
/* * Negotiate a krb5 gss context from the server end. */ static int gss_server( struct tcp_conn *rc) { OM_uint32 maj_stat, min_stat, ret_flags; gss_buffer_desc send_tok, recv_tok, AA; gss_OID doid; gss_name_t gss_name; gss_cred_id_t gss_creds; char *p, *realm, *msg; int rval = -1; int rvalue; char errbuf[256]; char *errmsg = NULL; auth_debug(1, "gss_server\n"); assert(rc != NULL); /* * We need to be root while in gss_acquire_cred() to read the host key * out of the default keytab. We also need to be root in * gss_accept_context() thanks to the replay cache code. */ if (!set_root_privs(0)) { g_snprintf(errbuf, SIZEOF(errbuf), _("can't take root privileges to read krb5 host key: %s"), strerror(errno)); goto out; } rc->gss_context = GSS_C_NO_CONTEXT; send_tok.value = vstralloc("host/", myhostname, NULL); send_tok.length = strlen(send_tok.value) + 1; for (p = send_tok.value; *p != '\0'; p++) { if (isupper((int)*p)) *p = tolower(*p); } maj_stat = gss_import_name(&min_stat, &send_tok, GSS_C_NULL_OID, &gss_name); if (maj_stat != (OM_uint32)GSS_S_COMPLETE) { set_root_privs(0); g_snprintf(errbuf, SIZEOF(errbuf), _("can't import name %s: %s"), (char *)send_tok.value, gss_error(maj_stat, min_stat)); amfree(send_tok.value); goto out; } amfree(send_tok.value); maj_stat = gss_display_name(&min_stat, gss_name, &AA, &doid); dbprintf(_("gss_name %s\n"), (char *)AA.value); maj_stat = gss_acquire_cred(&min_stat, gss_name, 0, GSS_C_NULL_OID_SET, GSS_C_ACCEPT, &gss_creds, NULL, NULL); if (maj_stat != (OM_uint32)GSS_S_COMPLETE) { g_snprintf(errbuf, SIZEOF(errbuf), _("can't acquire creds for host key host/%s: %s"), myhostname, gss_error(maj_stat, min_stat)); gss_release_name(&min_stat, &gss_name); set_root_privs(0); goto out; } gss_release_name(&min_stat, &gss_name); for (recv_tok.length = 0;;) { recv_tok.value = NULL; rvalue = tcpm_recv_token(rc, rc->read, &rc->handle, &rc->errmsg, /* (void *) is to avoid type-punning warning */ (char **)(void *)&recv_tok.value, (ssize_t *)&recv_tok.length, 60); if (rvalue <= 0) { if (rvalue < 0) { g_snprintf(errbuf, SIZEOF(errbuf), _("recv error in gss loop: %s"), rc->errmsg); amfree(rc->errmsg); } else g_snprintf(errbuf, SIZEOF(errbuf), _("EOF in gss loop")); goto out; } maj_stat = gss_accept_sec_context(&min_stat, &rc->gss_context, gss_creds, &recv_tok, GSS_C_NO_CHANNEL_BINDINGS, &gss_name, &doid, &send_tok, &ret_flags, NULL, NULL); if (maj_stat != (OM_uint32)GSS_S_COMPLETE && maj_stat != (OM_uint32)GSS_S_CONTINUE_NEEDED) { g_snprintf(errbuf, SIZEOF(errbuf), _("error accepting context: %s"), gss_error(maj_stat, min_stat)); amfree(recv_tok.value); goto out; } amfree(recv_tok.value); if (send_tok.length != 0 && tcpm_send_token(rc, rc->write, 0, &errmsg, send_tok.value, send_tok.length) < 0) { strncpy(errbuf, rc->errmsg, SIZEOF(errbuf) - 1); errbuf[SIZEOF(errbuf) - 1] = '\0'; amfree(rc->errmsg); gss_release_buffer(&min_stat, &send_tok); goto out; } gss_release_buffer(&min_stat, &send_tok); /* * If we need to get more from the client, then register for * more packets. */ if (maj_stat != (OM_uint32)GSS_S_CONTINUE_NEEDED) break; } maj_stat = gss_display_name(&min_stat, gss_name, &send_tok, &doid); if (maj_stat != (OM_uint32)GSS_S_COMPLETE) { g_snprintf(errbuf, SIZEOF(errbuf), _("can't display gss name: %s"), gss_error(maj_stat, min_stat)); gss_release_name(&min_stat, &gss_name); goto out; } gss_release_name(&min_stat, &gss_name); /* get rid of the realm */ if ((p = strchr(send_tok.value, '@')) == NULL) { g_snprintf(errbuf, SIZEOF(errbuf), _("malformed gss name: %s"), (char *)send_tok.value); amfree(send_tok.value); goto out; } *p = '\0'; realm = ++p; /* * If the principal doesn't match, complain */ if ((msg = krb5_checkuser(rc->hostname, send_tok.value, realm)) != NULL) { g_snprintf(errbuf, SIZEOF(errbuf), _("access not allowed from %s: %s"), (char *)send_tok.value, msg); amfree(send_tok.value); goto out; } amfree(send_tok.value); rval = 0; out: set_root_privs(0); if (rval != 0) { rc->errmsg = stralloc(errbuf); } else { rc->auth = 1; } auth_debug(1, _("gss_server returning %d\n"), rval); return (rval); }
/* * Negotiate a krb5 gss context from the client end. */ static int gss_client( struct sec_handle *rh) { struct sec_stream *rs = rh->rs; struct tcp_conn *rc = rs->rc; gss_buffer_desc send_tok, recv_tok, AA; gss_OID doid; OM_uint32 maj_stat, min_stat; unsigned int ret_flags; int rval = -1; int rvalue; gss_name_t gss_name; char *errmsg = NULL; auth_debug(1, "gss_client\n"); send_tok.value = vstralloc("host/", rs->rc->hostname, NULL); send_tok.length = strlen(send_tok.value) + 1; maj_stat = gss_import_name(&min_stat, &send_tok, GSS_C_NULL_OID, &gss_name); if (maj_stat != (OM_uint32)GSS_S_COMPLETE) { security_seterror(&rh->sech, _("can't import name %s: %s"), (char *)send_tok.value, gss_error(maj_stat, min_stat)); amfree(send_tok.value); return (-1); } amfree(send_tok.value); rc->gss_context = GSS_C_NO_CONTEXT; maj_stat = gss_display_name(&min_stat, gss_name, &AA, &doid); dbprintf(_("gss_name %s\n"), (char *)AA.value); /* * Perform the context-establishement loop. * * Every generated token is stored in send_tok which is then * transmitted to the server; every received token is stored in * recv_tok (empty on the first pass) to be processed by * the next call to gss_init_sec_context. * * GSS-API guarantees that send_tok's length will be non-zero * if and only if the server is expecting another token from us, * and that gss_init_sec_context returns GSS_S_CONTINUE_NEEDED if * and only if the server has another token to send us. */ recv_tok.value = NULL; for (recv_tok.length = 0;;) { min_stat = 0; maj_stat = gss_init_sec_context(&min_stat, GSS_C_NO_CREDENTIAL, &rc->gss_context, gss_name, GSS_C_NULL_OID, (OM_uint32)GSS_C_MUTUAL_FLAG|GSS_C_REPLAY_FLAG, 0, NULL, /* no channel bindings */ (recv_tok.length == 0 ? GSS_C_NO_BUFFER : &recv_tok), NULL, /* ignore mech type */ &send_tok, &ret_flags, NULL); /* ignore time_rec */ if (recv_tok.length != 0) { amfree(recv_tok.value); recv_tok.length = 0; } if (maj_stat != (OM_uint32)GSS_S_COMPLETE && maj_stat != (OM_uint32)GSS_S_CONTINUE_NEEDED) { security_seterror(&rh->sech, _("error getting gss context: %s %s"), gss_error(maj_stat, min_stat), (char *)send_tok.value); goto done; } /* * Send back the response */ if (send_tok.length != 0 && tcpm_send_token(rc, rc->write, rs->handle, &errmsg, send_tok.value, send_tok.length) < 0) { security_seterror(&rh->sech, "%s", rc->errmsg); gss_release_buffer(&min_stat, &send_tok); goto done; } gss_release_buffer(&min_stat, &send_tok); /* * If we need to continue, then register for more packets */ if (maj_stat != (OM_uint32)GSS_S_CONTINUE_NEEDED) break; rvalue = tcpm_recv_token(rc, rc->read, &rc->handle, &rc->errmsg, (void *)&recv_tok.value, (ssize_t *)&recv_tok.length, 60); if (rvalue <= 0) { if (rvalue < 0) security_seterror(&rh->sech, _("recv error in gss loop: %s"), rc->errmsg); else security_seterror(&rh->sech, _("EOF in gss loop")); goto done; } } rval = 0; rc->auth = 1; done: gss_release_name(&min_stat, &gss_name); return (rval); }
gss_client_response *authenticate_gss_client_wrap(gss_client_state* state, const char* challenge, const char* user) { OM_uint32 maj_stat; OM_uint32 min_stat; gss_buffer_desc input_token = GSS_C_EMPTY_BUFFER; gss_buffer_desc output_token = GSS_C_EMPTY_BUFFER; int ret = AUTH_GSS_CONTINUE; gss_client_response *response = NULL; char buf[4096], server_conf_flags; unsigned long buf_size; // Always clear out the old response if(state->response != NULL) { free(state->response); state->response = NULL; } if(challenge && *challenge) { int len; input_token.value = base64_decode(challenge, &len); input_token.length = len; } if(user) { // get bufsize server_conf_flags = ((char*) input_token.value)[0]; ((char*) input_token.value)[0] = 0; buf_size = ntohl(*((long *) input_token.value)); free(input_token.value); #ifdef PRINTFS printf("User: %s, %c%c%c\n", user, server_conf_flags & GSS_AUTH_P_NONE ? 'N' : '-', server_conf_flags & GSS_AUTH_P_INTEGRITY ? 'I' : '-', server_conf_flags & GSS_AUTH_P_PRIVACY ? 'P' : '-'); printf("Maximum GSS token size is %ld\n", buf_size); #endif // agree to terms (hack!) buf_size = htonl(buf_size); // not relevant without integrity/privacy memcpy(buf, &buf_size, 4); buf[0] = GSS_AUTH_P_NONE; // server decides if principal can log in as user strncpy(buf + 4, user, sizeof(buf) - 4); input_token.value = buf; input_token.length = 4 + strlen(user); } // Do GSSAPI wrap maj_stat = gss_wrap(&min_stat, state->context, 0, GSS_C_QOP_DEFAULT, &input_token, NULL, &output_token); if (maj_stat != GSS_S_COMPLETE) { response = gss_error(maj_stat, min_stat); response->return_code = AUTH_GSS_ERROR; goto end; } else ret = AUTH_GSS_COMPLETE; // Grab the client response to send back to the server if (output_token.length) { state->response = base64_encode((const unsigned char *)output_token.value, output_token.length);; maj_stat = gss_release_buffer(&min_stat, &output_token); } end: if (output_token.value) gss_release_buffer(&min_stat, &output_token); if(response == NULL) { response = calloc(1, sizeof(gss_client_response)); response->return_code = ret; } // Return the response return response; }
gss_client_response *authenticate_gss_client_step(gss_client_state* state, const char* challenge) { OM_uint32 maj_stat; OM_uint32 min_stat; gss_buffer_desc input_token = GSS_C_EMPTY_BUFFER; gss_buffer_desc output_token = GSS_C_EMPTY_BUFFER; int ret = AUTH_GSS_CONTINUE; gss_client_response *response = NULL; // Always clear out the old response if (state->response != NULL) { free(state->response); state->response = NULL; } // If there is a challenge (data from the server) we need to give it to GSS if (challenge && *challenge) { int len; input_token.value = base64_decode(challenge, &len); input_token.length = len; } // Do GSSAPI step maj_stat = gss_init_sec_context(&min_stat, GSS_C_NO_CREDENTIAL, &state->context, state->server_name, GSS_C_NO_OID, (OM_uint32)state->gss_flags, 0, GSS_C_NO_CHANNEL_BINDINGS, &input_token, NULL, &output_token, NULL, NULL); if ((maj_stat != GSS_S_COMPLETE) && (maj_stat != GSS_S_CONTINUE_NEEDED)) { response = gss_error(maj_stat, min_stat); response->return_code = AUTH_GSS_ERROR; goto end; } ret = (maj_stat == GSS_S_COMPLETE) ? AUTH_GSS_COMPLETE : AUTH_GSS_CONTINUE; // Grab the client response to send back to the server if(output_token.length) { state->response = base64_encode((const unsigned char *)output_token.value, output_token.length); maj_stat = gss_release_buffer(&min_stat, &output_token); } // Try to get the user name if we have completed all GSS operations if (ret == AUTH_GSS_COMPLETE) { gss_name_t gssuser = GSS_C_NO_NAME; maj_stat = gss_inquire_context(&min_stat, state->context, &gssuser, NULL, NULL, NULL, NULL, NULL, NULL); if(GSS_ERROR(maj_stat)) { response = gss_error(maj_stat, min_stat); response->return_code = AUTH_GSS_ERROR; goto end; } gss_buffer_desc name_token; name_token.length = 0; maj_stat = gss_display_name(&min_stat, gssuser, &name_token, NULL); if(GSS_ERROR(maj_stat)) { if(name_token.value) gss_release_buffer(&min_stat, &name_token); gss_release_name(&min_stat, &gssuser); response = gss_error(maj_stat, min_stat); response->return_code = AUTH_GSS_ERROR; goto end; } else { state->username = (char *)malloc(name_token.length + 1); strncpy(state->username, (char*) name_token.value, name_token.length); state->username[name_token.length] = 0; gss_release_buffer(&min_stat, &name_token); gss_release_name(&min_stat, &gssuser); } } end: if(output_token.value) gss_release_buffer(&min_stat, &output_token); if(input_token.value) free(input_token.value); if(response == NULL) { response = calloc(1, sizeof(gss_client_response)); response->return_code = ret; } // Return the response return response; }
gss_client_response *authenticate_gss_server_step(gss_server_state *state, const char *auth_data) { OM_uint32 maj_stat; OM_uint32 min_stat; gss_buffer_desc input_token = GSS_C_EMPTY_BUFFER; gss_buffer_desc output_token = GSS_C_EMPTY_BUFFER; int ret = AUTH_GSS_CONTINUE; gss_client_response *response = NULL; // Always clear out the old response if (state->response != NULL) { free(state->response); state->response = NULL; } // we don't need to check the authentication token if S4U2Self protocol // transition was done, because we already have the client credentials. if (state->client_creds == GSS_C_NO_CREDENTIAL) { if (auth_data && *auth_data) { int len; input_token.value = base64_decode(auth_data, &len); input_token.length = len; } else { response = calloc(1, sizeof(gss_client_response)); if(response == NULL) die1("Memory allocation failed"); response->message = strdup("No auth_data value in request from client"); response->return_code = AUTH_GSS_ERROR; goto end; } maj_stat = gss_accept_sec_context(&min_stat, &state->context, state->server_creds, &input_token, GSS_C_NO_CHANNEL_BINDINGS, &state->client_name, NULL, &output_token, NULL, NULL, &state->client_creds); if (GSS_ERROR(maj_stat)) { response = gss_error(__func__, "gss_accept_sec_context", maj_stat, min_stat); response->return_code = AUTH_GSS_ERROR; goto end; } // Grab the server response to send back to the client if (output_token.length) { state->response = base64_encode((const unsigned char *)output_token.value, output_token.length); maj_stat = gss_release_buffer(&min_stat, &output_token); } } // Get the user name maj_stat = gss_display_name(&min_stat, state->client_name, &output_token, NULL); if (GSS_ERROR(maj_stat)) { response = gss_error(__func__, "gss_display_name", maj_stat, min_stat); response->return_code = AUTH_GSS_ERROR; goto end; } state->username = (char *)malloc(output_token.length + 1); strncpy(state->username, (char*) output_token.value, output_token.length); state->username[output_token.length] = 0; // Get the target name if no server creds were supplied if (state->server_creds == GSS_C_NO_CREDENTIAL) { gss_name_t target_name = GSS_C_NO_NAME; maj_stat = gss_inquire_context(&min_stat, state->context, NULL, &target_name, NULL, NULL, NULL, NULL, NULL); if (GSS_ERROR(maj_stat)) { response = gss_error(__func__, "gss_inquire_context", maj_stat, min_stat); response->return_code = AUTH_GSS_ERROR; goto end; } maj_stat = gss_display_name(&min_stat, target_name, &output_token, NULL); if (GSS_ERROR(maj_stat)) { response = gss_error(__func__, "gss_display_name", maj_stat, min_stat); response->return_code = AUTH_GSS_ERROR; goto end; } state->targetname = (char *)malloc(output_token.length + 1); strncpy(state->targetname, (char*) output_token.value, output_token.length); state->targetname[output_token.length] = 0; } if (state->constrained_delegation && state->client_creds != GSS_C_NO_CREDENTIAL) { if ((response = store_gss_creds(state)) != NULL) { goto end; } } ret = AUTH_GSS_COMPLETE; end: if (output_token.length) gss_release_buffer(&min_stat, &output_token); if (input_token.value) free(input_token.value); if(response == NULL) { response = calloc(1, sizeof(gss_client_response)); if(response == NULL) die1("Memory allocation failed"); response->return_code = ret; } // Return the response return response; }
gss_client_response *authenticate_gss_server_init(const char *service, bool constrained_delegation, const char *username, gss_server_state *state) { OM_uint32 maj_stat; OM_uint32 min_stat; gss_buffer_desc name_token = GSS_C_EMPTY_BUFFER; int ret = AUTH_GSS_COMPLETE; gss_client_response *response = NULL; gss_cred_usage_t usage = GSS_C_ACCEPT; state->context = GSS_C_NO_CONTEXT; state->server_name = GSS_C_NO_NAME; state->client_name = GSS_C_NO_NAME; state->server_creds = GSS_C_NO_CREDENTIAL; state->client_creds = GSS_C_NO_CREDENTIAL; state->username = NULL; state->targetname = NULL; state->response = NULL; state->constrained_delegation = constrained_delegation; state->delegated_credentials_cache = NULL; // Server name may be empty which means we aren't going to create our own creds size_t service_len = strlen(service); if (service_len != 0) { // Import server name first name_token.length = strlen(service); name_token.value = (char *)service; maj_stat = gss_import_name(&min_stat, &name_token, GSS_C_NT_HOSTBASED_SERVICE, &state->server_name); if (GSS_ERROR(maj_stat)) { response = gss_error(__func__, "gss_import_name", maj_stat, min_stat); response->return_code = AUTH_GSS_ERROR; goto end; } if (state->constrained_delegation) { usage = GSS_C_BOTH; } // Get credentials maj_stat = gss_acquire_cred(&min_stat, state->server_name, GSS_C_INDEFINITE, GSS_C_NO_OID_SET, usage, &state->server_creds, NULL, NULL); if (GSS_ERROR(maj_stat)) { response = gss_error(__func__, "gss_acquire_cred", maj_stat, min_stat); response->return_code = AUTH_GSS_ERROR; goto end; } } // If a username was passed, perform the S4U2Self protocol transition to acquire // a credentials from that user as if we had done gss_accept_sec_context. // In this scenario, the passed username is assumed to be already authenticated // by some external mechanism, and we are here to "bootstrap" some gss credentials. // In authenticate_gss_server_step we will bypass the actual authentication step. if (username != NULL) { gss_name_t gss_username; name_token.length = strlen(username); name_token.value = (char *)username; maj_stat = gss_import_name(&min_stat, &name_token, GSS_C_NT_USER_NAME, &gss_username); if (GSS_ERROR(maj_stat)) { response = gss_error(__func__, "gss_import_name", maj_stat, min_stat); response->return_code = AUTH_GSS_ERROR; goto end; } maj_stat = gss_acquire_cred_impersonate_name(&min_stat, state->server_creds, gss_username, GSS_C_INDEFINITE, GSS_C_NO_OID_SET, GSS_C_INITIATE, &state->client_creds, NULL, NULL); if (GSS_ERROR(maj_stat)) { response = gss_error(__func__, "gss_acquire_cred_impersonate_name", maj_stat, min_stat); response->return_code = AUTH_GSS_ERROR; } gss_release_name(&min_stat, &gss_username); if (response != NULL) { goto end; } // because the username MAY be a "local" username, // we want get the canonical name from the acquired creds. maj_stat = gss_inquire_cred(&min_stat, state->client_creds, &state->client_name, NULL, NULL, NULL); if (GSS_ERROR(maj_stat)) { response = gss_error(__func__, "gss_inquire_cred", maj_stat, min_stat); response->return_code = AUTH_GSS_ERROR; goto end; } } end: if(response == NULL) { response = calloc(1, sizeof(gss_client_response)); if(response == NULL) die1("Memory allocation failed"); response->return_code = ret; } // Return the response return response; }