int main(int argc, char *argv[]) { OM_uint32 minor, major, flags; gss_OID mech = &mech_krb5; gss_name_t tname; gss_buffer_desc itok, atok; gss_ctx_id_t ictx = GSS_C_NO_CONTEXT, actx = GSS_C_NO_CONTEXT; if (argc != 2) { fprintf(stderr, "Usage: %s targetname\n", argv[0]); return 1; } tname = import_name(argv[1]); /* Get the initial context token. */ flags = GSS_C_REPLAY_FLAG | GSS_C_SEQUENCE_FLAG | GSS_C_MUTUAL_FLAG; major = gss_init_sec_context(&minor, GSS_C_NO_CREDENTIAL, &ictx, tname, mech, flags, GSS_C_INDEFINITE, GSS_C_NO_CHANNEL_BINDINGS, GSS_C_NO_BUFFER, NULL, &itok, NULL, NULL); check_gsserr("gss_init_sec_context(1)", major, minor); assert(major == GSS_S_CONTINUE_NEEDED); /* Process this token into an acceptor context, then discard it. */ major = gss_accept_sec_context(&minor, &actx, GSS_C_NO_CREDENTIAL, &itok, GSS_C_NO_CHANNEL_BINDINGS, NULL, NULL, &atok, NULL, NULL, NULL); check_gsserr("gss_accept_sec_context(1)", major, minor); (void)gss_release_buffer(&minor, &atok); (void)gss_delete_sec_context(&minor, &actx, NULL); /* Process the same token again, producing a replay error. */ major = gss_accept_sec_context(&minor, &actx, GSS_C_NO_CREDENTIAL, &itok, GSS_C_NO_CHANNEL_BINDINGS, NULL, NULL, &atok, NULL, NULL, NULL); check_replay_error("gss_accept_sec_context(2)", major, minor); assert(atok.length != 0); /* Send the error token back the initiator. */ (void)gss_release_buffer(&minor, &itok); major = gss_init_sec_context(&minor, GSS_C_NO_CREDENTIAL, &ictx, tname, mech, flags, GSS_C_INDEFINITE, GSS_C_NO_CHANNEL_BINDINGS, &atok, NULL, &itok, NULL, NULL); check_replay_error("gss_init_sec_context(2)", major, minor); (void)gss_release_name(&minor, &tname); (void)gss_release_buffer(&minor, &itok); (void)gss_release_buffer(&minor, &atok); (void)gss_delete_sec_context(&minor, &ictx, NULL); (void)gss_delete_sec_context(&minor, &actx, NULL); return 0; }
uint32_t sapgss_accept_sec_context( uint32_t *minor_status, gss_ctx_id_t *context_handle, gss_cred_id_t acceptor_cred_handle, gss_buffer_t input_token_buffer, sapgss_channel_bindings_t input_chan_bindings, gss_name_t *src_name, sapgss_OID *mech_type, gss_buffer_t output_token, uint32_t *ret_flags, uint32_t *time_rec, gss_cred_id_t *delegated_cred_handle) { gss_OID mech_type_loc; uint32_t major_status; int ret; memset(&mech_type_loc, 0, sizeof(mech_type_loc)); if (input_chan_bindings != NULL) return GSS_S_FAILURE; major_status = gss_accept_sec_context(minor_status, context_handle, acceptor_cred_handle, input_token_buffer, NULL, src_name, &mech_type_loc, output_token, ret_flags, time_rec, delegated_cred_handle); ret = gss_OID_loc_to_sap(mech_type_loc, mech_type); if (ret != 0) { *minor_status = ret; return GSS_S_FAILURE; } return major_status; }
/*-------------*/ static int accept_sec_context (gss_ctx_id_t *context, gss_cred_id_t server_creds, gss_buffer_desc *ticket_buffer, gss_name_t *client_name, gss_buffer_desc *authenticator_buff) { OM_uint32 major_status = 0, minor_status = 0, ret_flags; /* Initialize autheticator buffer. */ authenticator_buff->length = 0; authenticator_buff->value = NULL; LOG(log_debug, logtype_uams, "accept_context: accepting context (ticketlen: %u)", ticket_buffer->length); /* * Try to accept the secondary context using the tocken in ticket_buffer. * We don't care about the mechanisms used, nor for the time. * We don't act as a proxy either. */ major_status = gss_accept_sec_context( &minor_status, context, server_creds, ticket_buffer, GSS_C_NO_CHANNEL_BINDINGS, client_name, NULL, authenticator_buff, &ret_flags, NULL, NULL ); if (major_status != GSS_S_COMPLETE) { log_status( "accept_sec_context", major_status, minor_status ); return 1; } log_ctx_flags( ret_flags ); return 0; }
/* Privileged */ OM_uint32 ssh_gssapi_accept_ctx(Gssctxt *ctx, gss_buffer_desc *recv_tok, gss_buffer_desc *send_tok, OM_uint32 *flags) { OM_uint32 status; gss_OID mech; ctx->major = gss_accept_sec_context(&ctx->minor, &ctx->context, ctx->creds, recv_tok, GSS_C_NO_CHANNEL_BINDINGS, &ctx->client, &mech, send_tok, flags, NULL, &ctx->client_creds); if (GSS_ERROR(ctx->major)) ssh_gssapi_error(ctx); if (ctx->client_creds) debug("Received some client credentials"); else debug("Got no client credentials"); status = ctx->major; /* Now, if we're complete and we have the right flags, then * we flag the user as also having been authenticated */ if (((flags == NULL) || ((*flags & GSS_C_MUTUAL_FLAG) && (*flags & GSS_C_INTEG_FLAG))) && (ctx->major == GSS_S_COMPLETE)) { if (ssh_gssapi_getclient(ctx, &gssapi_client)) fatal("Couldn't convert client name"); } return (status); }
/* * Call gss_accept_context, with token just read from the wire. */ int gssapi_get_rtoken(struct ph1handle *iph1, int *lenp) { struct gssapi_ph1_state *gps; gss_buffer_desc name_token; gss_buffer_t itoken, rtoken; OM_uint32 min_stat, maj_stat; gss_name_t client_name; if (gssapi_get_state(iph1) == NULL && gssapi_init(iph1) < 0) return -1; gps = gssapi_get_state(iph1); rtoken = &gps->gss_p[gps->gsscnt_p - 1]; itoken = &gps->gss[gps->gsscnt]; gps->gss_status = gss_accept_sec_context(&min_stat, &gps->gss_context, gps->gss_cred, rtoken, GSS_C_NO_CHANNEL_BINDINGS, &client_name, NULL, itoken, NULL, NULL, NULL); if (GSS_ERROR(gps->gss_status)) { gssapi_error(min_stat, LOCATION, "accept_sec_context\n"); return -1; } maj_stat = gss_display_name(&min_stat, client_name, &name_token, NULL); if (GSS_ERROR(maj_stat)) { gssapi_error(min_stat, LOCATION, "gss_display_name\n"); maj_stat = gss_release_name(&min_stat, &client_name); if (GSS_ERROR(maj_stat)) gssapi_error(min_stat, LOCATION, "release client_name\n"); return -1; } maj_stat = gss_release_name(&min_stat, &client_name); if (GSS_ERROR(maj_stat)) gssapi_error(min_stat, LOCATION, "release client_name\n"); plog(LLV_DEBUG, LOCATION, NULL, "gss_accept_sec_context: other side is %s\n", (char *)name_token.value); maj_stat = gss_release_buffer(&min_stat, &name_token); if (GSS_ERROR(maj_stat)) gssapi_error(min_stat, LOCATION, "release name buffer\n"); if (itoken->length != 0) gps->gsscnt++; if (lenp) *lenp = itoken->length; return 0; }
void zmq::gssapi_server_t::accept_context () { maj_stat = gss_accept_sec_context(&init_sec_min_stat, &context, cred, &recv_tok, GSS_C_NO_CHANNEL_BINDINGS, &target_name, &doid, &send_tok, &ret_flags, NULL, NULL); if (recv_tok.value) { free (recv_tok.value); recv_tok.value = NULL; } }
static int accept_sec_context(gss_ctx_id_t *context, gss_buffer_desc *ticket_buffer, gss_name_t *client_name, gss_buffer_desc *authenticator_buff) { OM_uint32 major_status = 0, minor_status = 0, flags = 0; /* Initialize autheticator buffer. */ authenticator_buff->length = 0; authenticator_buff->value = NULL; LOG_LOGINCONT(log_debug, "accepting context (ticketlen: %u)", ticket_buffer->length); /* * Try to accept the secondary context using the token in ticket_buffer. * We don't care about the principals or mechanisms used, nor for the time. * We don't act as a proxy either. */ major_status = gss_accept_sec_context(&minor_status, context, GSS_C_NO_CREDENTIAL, ticket_buffer, GSS_C_NO_CHANNEL_BINDINGS, client_name, NULL, authenticator_buff, &flags, NULL, NULL); if (major_status != GSS_S_COMPLETE) { log_status("gss_accept_sec_context", major_status, minor_status); return 1; } log_ctx_flags(flags); return 0; }
void establish_contexts(gss_OID imech, gss_cred_id_t icred, gss_cred_id_t acred, gss_name_t tname, OM_uint32 flags, gss_ctx_id_t *ictx, gss_ctx_id_t *actx, gss_name_t *src_name, gss_OID *amech, gss_cred_id_t *deleg_cred) { OM_uint32 minor, imaj, amaj; gss_buffer_desc itok, atok; *ictx = *actx = GSS_C_NO_CONTEXT; imaj = amaj = GSS_S_CONTINUE_NEEDED; itok.value = atok.value = NULL; itok.length = atok.length = 0; for (;;) { (void)gss_release_buffer(&minor, &itok); imaj = gss_init_sec_context(&minor, icred, ictx, tname, imech, flags, GSS_C_INDEFINITE, GSS_C_NO_CHANNEL_BINDINGS, &atok, NULL, &itok, NULL, NULL); check_gsserr("gss_init_sec_context", imaj, minor); if (amaj == GSS_S_COMPLETE) break; (void)gss_release_buffer(&minor, &atok); amaj = gss_accept_sec_context(&minor, actx, acred, &itok, GSS_C_NO_CHANNEL_BINDINGS, src_name, amech, &atok, NULL, NULL, deleg_cred); check_gsserr("gss_accept_sec_context", amaj, minor); (void)gss_release_buffer(&minor, &itok); if (imaj == GSS_S_COMPLETE) break; } if (imaj != GSS_S_COMPLETE || amaj != GSS_S_COMPLETE) errout("One side wants to continue after the other is done"); (void)gss_release_buffer(&minor, &itok); (void)gss_release_buffer(&minor, &atok); }
static pam_handle_t * perform_gssapi (void) { struct pam_conv conv = { pam_conv_func, }; OM_uint32 major, minor; gss_cred_id_t server = GSS_C_NO_CREDENTIAL; gss_cred_id_t client = GSS_C_NO_CREDENTIAL; gss_buffer_desc input = GSS_C_EMPTY_BUFFER; gss_buffer_desc output = GSS_C_EMPTY_BUFFER; gss_buffer_desc local = GSS_C_EMPTY_BUFFER; gss_buffer_desc display = GSS_C_EMPTY_BUFFER; gss_name_t name = GSS_C_NO_NAME; gss_ctx_id_t context = GSS_C_NO_CONTEXT; gss_OID mech_type = GSS_C_NO_OID; pam_handle_t *pamh = NULL; OM_uint32 flags = 0; char *str = NULL; OM_uint32 caps = 0; int res; server = GSS_C_NO_CREDENTIAL; res = PAM_AUTH_ERR; /* We shouldn't be writing to kerberos caches here */ setenv ("KRB5CCNAME", "FILE:/dev/null", 1); debug ("reading kerberos auth from cockpit-ws"); input.value = read_auth_until_eof (&input.length); major = gss_accept_sec_context (&minor, &context, server, &input, GSS_C_NO_CHANNEL_BINDINGS, &name, &mech_type, &output, &flags, &caps, &client); if (GSS_ERROR (major)) { warnx ("gssapi auth failed: %s", gssapi_strerror (major, minor)); goto out; } /* * In general gssapi mechanisms can require multiple challenge response * iterations keeping &context between each, however Kerberos doesn't * require this, so we don't care :O * * If we ever want this to work with something other than Kerberos, then * we'll have to have some sorta session that holds the context. */ if (major & GSS_S_CONTINUE_NEEDED) goto out; major = gss_localname (&minor, name, mech_type, &local); if (major == GSS_S_COMPLETE) { minor = 0; str = dup_string (local.value, local.length); debug ("mapped gssapi name to local user '%s'", str); if (getpwnam (str)) { res = pam_start ("cockpit", str, &conv, &pamh); } else { /* If the local user doesn't exist, pretend gss_localname() failed */ free (str); str = NULL; major = GSS_S_FAILURE; minor = KRB5_NO_LOCALNAME; } } if (major != GSS_S_COMPLETE) { if (minor == (OM_uint32)KRB5_NO_LOCALNAME || minor == (OM_uint32)KRB5_LNAME_NOTRANS) { major = gss_display_name (&minor, name, &display, NULL); if (GSS_ERROR (major)) { warnx ("couldn't get gssapi display name: %s", gssapi_strerror (major, minor)); goto out; } str = dup_string (display.value, display.length); debug ("no local user mapping for gssapi name '%s'", str); res = pam_start ("cockpit", str, &conv, &pamh); } else { warnx ("couldn't map gssapi name to local user: %s", gssapi_strerror (major, minor)); goto out; } } if (res != PAM_SUCCESS) errx (EX, "couldn't start pam: %s", pam_strerror (NULL, res)); if (pam_set_item (pamh, PAM_RHOST, rhost) != PAM_SUCCESS || pam_get_item (pamh, PAM_USER, (const void **)&user) != PAM_SUCCESS) errx (EX, "couldn't setup pam"); assert (user != NULL); res = open_session (pamh, user); out: write_auth_begin (res); if (user) write_auth_string ("user", user); if (output.value) write_auth_hex ("gssapi-output", output.value, output.length); if (output.value) gss_release_buffer (&minor, &output); output.value = NULL; output.length = 0; if (caps & GSS_C_DELEG_FLAG && client != GSS_C_NO_CREDENTIAL) { #ifdef HAVE_GSS_IMPORT_CRED major = gss_export_cred (&minor, client, &output); if (GSS_ERROR (major)) warnx ("couldn't export gssapi credentials: %s", gssapi_strerror (major, minor)); else if (output.value) write_auth_hex ("gssapi-creds", output.value, output.length); #else /* cockpit-ws will complain for us, if they're ever used */ write_auth_hex ("gssapi-creds", (void *)"", 0); #endif } write_auth_end (); if (display.value) gss_release_buffer (&minor, &display); if (output.value) gss_release_buffer (&minor, &output); if (local.value) gss_release_buffer (&minor, &local); if (client != GSS_C_NO_CREDENTIAL) gss_release_cred (&minor, &client); if (server != GSS_C_NO_CREDENTIAL) gss_release_cred (&minor, &server); if (name != GSS_C_NO_NAME) gss_release_name (&minor, &name); if (context != GSS_C_NO_CONTEXT) gss_delete_sec_context (&minor, &context, GSS_C_NO_BUFFER); free (input.value); free (str); unsetenv ("KRB5CCNAME"); if (res != PAM_SUCCESS) exit (5); return pamh; }
static bool svcauth_gss_accept_sec_context(struct svc_req *req, struct svc_rpc_gss_data *gd, struct rpc_gss_init_res *gr) { struct rpc_gss_cred *gc; gss_buffer_desc recv_tok, seqbuf, checksum; gss_OID mech; OM_uint32 maj_stat = 0, min_stat = 0, ret_flags, seq; #define INDEF_EXPIRE 60*60*24 /* from mit k5 src/lib/rpc/svc_auth_gssapi.c */ OM_uint32 time_rec; gc = (struct rpc_gss_cred *)req->rq_clntcred; memset(gr, 0, sizeof(*gr)); /* Deserialize arguments. */ memset(&recv_tok, 0, sizeof(recv_tok)); if (!svc_getargs (req->rq_xprt, req, (xdrproc_t) xdr_rpc_gss_init_args, (caddr_t) &recv_tok, NULL /* u_data */)) return (false); gr->gr_major = gss_accept_sec_context(&gr->gr_minor, &gd->ctx, svcauth_gss_creds, &recv_tok, GSS_C_NO_CHANNEL_BINDINGS, &gd->client_name, &mech, &gr->gr_token, &ret_flags, &time_rec, NULL); svc_freeargs(req->rq_xprt, req, (xdrproc_t) xdr_rpc_gss_init_args, (caddr_t) &recv_tok); if ((gr->gr_major != GSS_S_COMPLETE) && (gr->gr_major != GSS_S_CONTINUE_NEEDED)) { __warnx(TIRPC_DEBUG_FLAG_AUTH, "%s: auth failed major=%u minor=%u", __func__, gr->gr_major, gr->gr_minor); gd->ctx = GSS_C_NO_CONTEXT; gss_release_buffer(&min_stat, &gr->gr_token); return (false); } /* ANDROS: krb5 mechglue returns ctx of size 8 - two pointers, * one to the mechanism oid, one to the internal_ctx_id */ gr->gr_ctx.value = mem_alloc(sizeof(gss_union_ctx_id_desc)); memcpy(gr->gr_ctx.value, gd->ctx, sizeof(gss_union_ctx_id_desc)); gr->gr_ctx.length = sizeof(gss_union_ctx_id_desc); /* ANDROS: change for debugging linux kernel version... gr->gr_win = 0x00000005; */ gr->gr_win = sizeof(gd->seqmask) * 8; /* Save client info. */ gd->sec.mech = mech; gd->sec.qop = GSS_C_QOP_DEFAULT; gd->sec.svc = gc->gc_svc; gd->win = gr->gr_win; if (time_rec == GSS_C_INDEFINITE) time_rec = INDEF_EXPIRE; if (time_rec > 10) time_rec -= 5; gd->endtime = time_rec + get_time_fast(); if (gr->gr_major == GSS_S_COMPLETE) { maj_stat = gss_display_name(&min_stat, gd->client_name, &gd->cname, &gd->sec.mech); if (maj_stat != GSS_S_COMPLETE) { __warnx(TIRPC_DEBUG_FLAG_AUTH, "%s: display_name major=%u minor=%u", __func__, maj_stat, min_stat); gss_release_buffer(&min_stat, &gr->gr_token); return (false); } #ifdef DEBUG #ifdef HAVE_KRB5 { gss_buffer_desc mechname; gss_oid_to_str(&min_stat, mech, &mechname); __warnx(TIRPC_DEBUG_FLAG_AUTH, "%s: accepted context for %.*s with " "<mech %.*s, qop %d, svc %d>", __func__, gd->cname.length, (char *)gd->cname.value, mechname.length, (char *)mechname.value, gd->sec.qop, gd->sec.svc); gss_release_buffer(&min_stat, &mechname); } #elif HAVE_HEIMDAL __warnx(TIRPC_DEBUG_FLAG_AUTH, "%s: accepted context for %.*s with " "<mech {}, qop %d, svc %d>", __func__, gd->cname.length, (char *)gd->cname.value, gd->sec.qop, gd->sec.svc); #endif #endif /* DEBUG */ seq = htonl(gr->gr_win); seqbuf.value = &seq; seqbuf.length = sizeof(seq); gss_release_buffer(&min_stat, &gd->checksum); maj_stat = gss_sign(&min_stat, gd->ctx, GSS_C_QOP_DEFAULT, &seqbuf, &checksum); if (maj_stat != GSS_S_COMPLETE) { gss_release_buffer(&min_stat, &gr->gr_token); return (false); } /* XXX ref? (assert gd->locked?) */ req->rq_verf.oa_flavor = RPCSEC_GSS; req->rq_verf.oa_base = checksum.value; req->rq_verf.oa_length = checksum.length; } return (true); }
int main() { OM_uint32 init_maj_stat; OM_uint32 accept_maj_stat; OM_uint32 maj_stat; OM_uint32 min_stat; OM_uint32 ret_flags; OM_uint32 req_flags = 0; OM_uint32 time_rec; gss_buffer_desc send_tok; gss_buffer_desc recv_tok; gss_buffer_desc * token_ptr; gss_OID mech_type; gss_name_t target_name; gss_ctx_id_t init_context; gss_ctx_id_t accept_context; gss_ctx_id_t del_init_context; gss_ctx_id_t del_accept_context; gss_cred_id_t delegated_cred; gss_cred_id_t imported_cred; gss_cred_id_t cred_handle; char * error_str; globus_result_t result; globus_gsi_cert_utils_cert_type_t cert_type; int rc = EXIT_SUCCESS; printf("1..1\n"); /* Activate Modules */ globus_module_activate(GLOBUS_GSI_GSSAPI_MODULE); /* Initialize variables */ token_ptr = GSS_C_NO_BUFFER; init_context = GSS_C_NO_CONTEXT; accept_context = GSS_C_NO_CONTEXT; del_init_context = GSS_C_NO_CONTEXT; del_accept_context = GSS_C_NO_CONTEXT; delegated_cred = GSS_C_NO_CREDENTIAL; accept_maj_stat = GSS_S_CONTINUE_NEEDED; ret_flags = 0; req_flags |= GSS_C_GLOBUS_LIMITED_DELEG_PROXY_FLAG; /* acquire the credential */ maj_stat = gss_acquire_cred(&min_stat, NULL, GSS_C_INDEFINITE, GSS_C_NO_OID_SET, GSS_C_BOTH, &cred_handle, NULL, NULL); if(maj_stat != GSS_S_COMPLETE) { globus_gsi_gssapi_test_print_error(stderr, maj_stat, min_stat); rc = EXIT_FAILURE; goto fail; } /* get the subject name */ maj_stat = gss_inquire_cred(&min_stat, cred_handle, &target_name, NULL, NULL, NULL); if(maj_stat != GSS_S_COMPLETE) { globus_gsi_gssapi_test_print_error(stderr, maj_stat, min_stat); rc = EXIT_FAILURE; goto fail; } /* set up the first security context */ init_maj_stat = gss_init_sec_context(&min_stat, cred_handle, &init_context, target_name, GSS_C_NULL_OID, 0, 0, GSS_C_NO_CHANNEL_BINDINGS, token_ptr, NULL, &send_tok, NULL, NULL); if(init_maj_stat != GSS_S_CONTINUE_NEEDED) { globus_gsi_gssapi_test_print_error(stderr, init_maj_stat, min_stat); rc = EXIT_FAILURE; goto fail; } while(1) { accept_maj_stat=gss_accept_sec_context(&min_stat, &accept_context, GSS_C_NO_CREDENTIAL, &send_tok, GSS_C_NO_CHANNEL_BINDINGS, NULL, &mech_type, &recv_tok, &ret_flags, /* ignore time_rec */ NULL, NULL); if(accept_maj_stat != GSS_S_COMPLETE && accept_maj_stat != GSS_S_CONTINUE_NEEDED) { globus_gsi_gssapi_test_print_error(stderr, accept_maj_stat, min_stat); rc = EXIT_FAILURE; goto fail; } else if(accept_maj_stat == GSS_S_COMPLETE) { break; } init_maj_stat = gss_init_sec_context(&min_stat, GSS_C_NO_CREDENTIAL, &init_context, target_name, GSS_C_NULL_OID, 0, 0, GSS_C_NO_CHANNEL_BINDINGS, &recv_tok, NULL, &send_tok, NULL, NULL); if(init_maj_stat != GSS_S_COMPLETE && init_maj_stat != GSS_S_CONTINUE_NEEDED) { globus_gsi_gssapi_test_print_error(stderr, init_maj_stat, min_stat); rc = EXIT_FAILURE; goto fail; } } printf("# %s:%d: Successfully established initial security context\n", __FILE__, __LINE__); /* delegate our credential over the initial security context and * insert a restriction extension into the delegated credential. * This is a post GT 2.0 feature. */ init_maj_stat = gss_init_delegation(&min_stat, init_context, cred_handle, GSS_C_NO_OID, GSS_C_NO_OID_SET, GSS_C_NO_BUFFER_SET, token_ptr, req_flags, 0, &send_tok); if(init_maj_stat != GSS_S_COMPLETE && init_maj_stat != GSS_S_CONTINUE_NEEDED) { globus_gsi_gssapi_test_print_error(stderr, init_maj_stat, min_stat); rc = EXIT_FAILURE; goto fail; } while(1) { accept_maj_stat=gss_accept_delegation(&min_stat, accept_context, GSS_C_NO_OID_SET, GSS_C_NO_BUFFER_SET, &send_tok, req_flags, 0, &time_rec, &delegated_cred, &mech_type, &recv_tok); if(accept_maj_stat != GSS_S_COMPLETE && accept_maj_stat != GSS_S_CONTINUE_NEEDED) { globus_gsi_gssapi_test_print_error(stderr, accept_maj_stat, min_stat); rc = EXIT_FAILURE; goto fail; } else if(accept_maj_stat == GSS_S_COMPLETE) { break; } init_maj_stat = gss_init_delegation(&min_stat, init_context, cred_handle, GSS_C_NO_OID, GSS_C_NO_OID_SET, GSS_C_NO_BUFFER_SET, &recv_tok, req_flags, 0, &send_tok); if(init_maj_stat != GSS_S_COMPLETE && init_maj_stat != GSS_S_CONTINUE_NEEDED) { globus_gsi_gssapi_test_print_error(stderr, init_maj_stat, min_stat); rc = EXIT_FAILURE; goto fail; } } printf("# %s:%d: Successfully delegated credential\n", __FILE__, __LINE__); /* export and import the delegated credential */ /* this can be done both to a buffer and to a file */ /* New in GT 2.0 */ maj_stat = gss_export_cred(&min_stat, delegated_cred, GSS_C_NO_OID, 0, &send_tok); if(maj_stat != GSS_S_COMPLETE) { globus_gsi_gssapi_test_print_error(stderr, maj_stat, min_stat); rc = EXIT_FAILURE; goto fail; } maj_stat = gss_import_cred(&min_stat, &imported_cred, GSS_C_NO_OID, 0, &send_tok, 0, &time_rec); if(maj_stat != GSS_S_COMPLETE) { globus_gsi_gssapi_test_print_error(stderr, maj_stat, min_stat); rc = EXIT_FAILURE; goto fail; } printf("# %s:%d: Successfully exported/imported the delegated credential\n", __FILE__, __LINE__); /* set up another security context using the delegated credential */ init_maj_stat = gss_init_sec_context(&min_stat, imported_cred, &del_init_context, target_name, GSS_C_NULL_OID, 0, 0, GSS_C_NO_CHANNEL_BINDINGS, token_ptr, NULL, &send_tok, NULL, NULL); if(init_maj_stat != GSS_S_COMPLETE && init_maj_stat != GSS_S_CONTINUE_NEEDED) { globus_gsi_gssapi_test_print_error(stderr, init_maj_stat, min_stat); rc = EXIT_FAILURE; goto fail; } while(1) { accept_maj_stat=gss_accept_sec_context(&min_stat, &del_accept_context, imported_cred, &send_tok, GSS_C_NO_CHANNEL_BINDINGS, NULL, &mech_type, &recv_tok, &ret_flags, /* ignore time_rec */ NULL, NULL); if(accept_maj_stat != GSS_S_COMPLETE && accept_maj_stat != GSS_S_CONTINUE_NEEDED) { globus_gsi_gssapi_test_print_error(stderr, accept_maj_stat, min_stat); rc = EXIT_FAILURE; goto fail; } else if(accept_maj_stat == GSS_S_COMPLETE) { break; } init_maj_stat = gss_init_sec_context(&min_stat, imported_cred, &del_init_context, target_name, GSS_C_NULL_OID, 0, 0, GSS_C_NO_CHANNEL_BINDINGS, &recv_tok, NULL, &send_tok, NULL, NULL); if(init_maj_stat != GSS_S_COMPLETE && init_maj_stat != GSS_S_CONTINUE_NEEDED) { globus_gsi_gssapi_test_print_error(stderr, init_maj_stat, min_stat); rc = EXIT_FAILURE; goto fail; } } /* got sec context based on delegated cred now */ printf("# %s:%d: Successfully established security context with delegated credential\n", __FILE__, __LINE__); /* Verify that the delegated credential is a limited proxy */ result = globus_gsi_cred_get_cert_type( ((gss_cred_id_desc *)imported_cred)->cred_handle, &cert_type); if(result != GLOBUS_SUCCESS) { char * error_str; globus_object_t * error_obj; error_obj = globus_error_get(result); error_str = globus_error_print_chain(error_obj); fprintf(stderr, "%s", error_str); globus_libc_free(error_str); globus_object_free(error_obj); rc = EXIT_FAILURE; goto fail; } if (! GLOBUS_GSI_CERT_UTILS_IS_LIMITED_PROXY(cert_type)) { fprintf(stderr, "Invalid certificate type. Expected a limited proxy, got %d\n", (int) cert_type); rc = EXIT_FAILURE; goto fail; } fail: printf("%s gssapi_limited_delegation_test\n", (rc==EXIT_SUCCESS) ? "ok" : "not ok"); globus_module_deactivate_all(); exit(rc); }
OM_uint32 gssi_accept_sec_context(OM_uint32 *minor_status, gss_ctx_id_t *context_handle, gss_cred_id_t acceptor_cred_handle, gss_buffer_t input_token_buffer, gss_channel_bindings_t input_chan_bindings, gss_name_t *src_name, gss_OID *mech_type, gss_buffer_t output_token, OM_uint32 *ret_flags, OM_uint32 *time_rec, gss_cred_id_t *delegated_cred_handle) { enum gpp_behavior behavior; struct gpp_context_handle *ctx_handle = NULL; struct gpp_cred_handle *cred_handle = NULL; struct gpp_cred_handle *deleg_cred = NULL; struct gpp_name_handle *name = NULL; OM_uint32 maj, min; GSSI_TRACE(); behavior = gpp_get_behavior(); if (*context_handle) { ctx_handle = (struct gpp_context_handle *)*context_handle; if (ctx_handle->local) { /* if we already have a local context it means this is * a continuation, force local only behavior, nothing else * makes sense */ behavior = GPP_LOCAL_ONLY; } else if (ctx_handle->remote) { behavior = GPP_REMOTE_ONLY; } } else { ctx_handle = calloc(1, sizeof(struct gpp_context_handle)); if (!ctx_handle) { maj = GSS_S_FAILURE; min = ENOMEM; goto done; } } if (acceptor_cred_handle != GSS_C_NO_CREDENTIAL) { cred_handle = (struct gpp_cred_handle *)acceptor_cred_handle; } else { maj = gppint_get_def_creds(&min, behavior, NULL, GSS_C_ACCEPT, &cred_handle); if (maj != GSS_S_COMPLETE) { goto done; } } if (cred_handle->local) { if (behavior == GPP_REMOTE_ONLY) { min = 0; maj = GSS_S_DEFECTIVE_CREDENTIAL; goto done; } behavior = GPP_LOCAL_ONLY; } else if (cred_handle->remote) { if (behavior == GPP_LOCAL_ONLY) { min = 0; maj = GSS_S_DEFECTIVE_CREDENTIAL; goto done; } behavior = GPP_REMOTE_ONLY; } if (src_name) { name = calloc(1, sizeof(struct gpp_name_handle)); if (!name) { maj = GSS_S_FAILURE; min = ENOMEM; goto done; } } if (delegated_cred_handle) { deleg_cred = calloc(1, sizeof(struct gpp_cred_handle)); if (!deleg_cred) { maj = GSS_S_FAILURE; min = ENOMEM; goto done; } } /* behavior has been set to local only or remote only by context or * credential handler inspection, so we only have those 2 cases, * anything else is an error at this point. */ if (behavior == GPP_LOCAL_ONLY) { maj = gss_accept_sec_context(&min, &ctx_handle->local, cred_handle->local, input_token_buffer, input_chan_bindings, name ? &name->local : NULL, mech_type, output_token, ret_flags, time_rec, deleg_cred ? &deleg_cred->local : NULL); } else if (behavior == GPP_REMOTE_ONLY) { maj = gpm_accept_sec_context(&min, &ctx_handle->remote, cred_handle->remote, input_token_buffer, input_chan_bindings, name ? &name->remote : NULL, mech_type, output_token, ret_flags, time_rec, deleg_cred ? &deleg_cred->remote : NULL); } else { min = 0; maj = GSS_S_FAILURE; } done: *minor_status = gpp_map_error(min); if (maj != GSS_S_COMPLETE && maj != GSS_S_CONTINUE_NEEDED) { if (ctx_handle && ctx_handle->local == GSS_C_NO_CONTEXT && ctx_handle->remote == NULL) { free(ctx_handle); ctx_handle = NULL; } free(deleg_cred); free(name); } else { if (src_name) { *src_name = (gss_name_t)name; } if (delegated_cred_handle) { *delegated_cred_handle = (gss_cred_id_t)deleg_cred; } } /* always replace the provided context handle to avoid * dangling pointers when a context has been passed in */ *context_handle = (gss_ctx_id_t)ctx_handle; if (acceptor_cred_handle == GSS_C_NO_CREDENTIAL) { (void)gssi_release_cred(&min, (gss_cred_id_t *)&cred_handle); } return maj; }
static bool_t svcauth_gss_accept_sec_context(struct svc_req *rqst, struct rpc_gss_init_res *gr) { struct svc_rpc_gss_data *gd; struct rpc_gss_cred *gc; gss_buffer_desc recv_tok, seqbuf, checksum; gss_OID mech; OM_uint32 maj_stat = 0, min_stat = 0, ret_flags, seq; log_debug("in svcauth_gss_accept_context()"); gd = SVCAUTH_PRIVATE(rqst->rq_xprt->xp_auth); gc = (struct rpc_gss_cred *)rqst->rq_clntcred; memset(gr, 0, sizeof(*gr)); /* Deserialize arguments. */ memset(&recv_tok, 0, sizeof(recv_tok)); if (!svc_getargs(rqst->rq_xprt, (xdrproc_t)xdr_rpc_gss_init_args, (caddr_t)&recv_tok)) return (FALSE); gr->gr_major = gss_accept_sec_context(&gr->gr_minor, &gd->ctx, _svcauth_gss_creds, &recv_tok, GSS_C_NO_CHANNEL_BINDINGS, &gd->client_name, &mech, &gr->gr_token, &ret_flags, NULL, NULL); if (gr->gr_major != GSS_S_COMPLETE && gr->gr_major != GSS_S_CONTINUE_NEEDED) { log_status("accept_sec_context", gr->gr_major, gr->gr_minor); gd->ctx = GSS_C_NO_CONTEXT; gss_release_buffer(&min_stat, &gr->gr_token); return (FALSE); } /* ANDROS: krb5 mechglue returns ctx of size 8 - two pointers, * one to the mechanism oid, one to the internal_ctx_id */ if ((gr->gr_ctx.value = mem_alloc(sizeof(gss_union_ctx_id_desc))) == NULL) { fprintf(stderr, "svcauth_gss_accept_context: out of memory\n"); return (FALSE); } memcpy(gr->gr_ctx.value, gd->ctx, sizeof(gss_union_ctx_id_desc)); gr->gr_ctx.length = sizeof(gss_union_ctx_id_desc); /* ANDROS: change for debugging linux kernel version... gr->gr_win = sizeof(gd->seqmask) * 8; */ gr->gr_win = 0x00000005; /* Save client info. */ gd->sec.mech = mech; gd->sec.qop = GSS_C_QOP_DEFAULT; gd->sec.svc = gc->gc_svc; gd->seq = gc->gc_seq; gd->win = gr->gr_win; if (gr->gr_major == GSS_S_COMPLETE) { maj_stat = gss_display_name(&min_stat, gd->client_name, &gd->cname, &gd->sec.mech); if (maj_stat != GSS_S_COMPLETE) { log_status("display_name", maj_stat, min_stat); return (FALSE); } #ifdef DEBUG #ifdef HAVE_KRB5 { gss_buffer_desc mechname; gss_oid_to_str(&min_stat, mech, &mechname); log_debug("accepted context for %.*s with " "<mech %.*s, qop %d, svc %d>", gd->cname.length, (char *)gd->cname.value, mechname.length, (char *)mechname.value, gd->sec.qop, gd->sec.svc); gss_release_buffer(&min_stat, &mechname); } #elif HAVE_HEIMDAL log_debug("accepted context for %.*s with " "<mech {}, qop %d, svc %d>", gd->cname.length, (char *)gd->cname.value, gd->sec.qop, gd->sec.svc); #endif #endif /* DEBUG */ seq = htonl(gr->gr_win); seqbuf.value = &seq; seqbuf.length = sizeof(seq); maj_stat = gss_sign(&min_stat, gd->ctx, GSS_C_QOP_DEFAULT, &seqbuf, &checksum); if (maj_stat != GSS_S_COMPLETE) return (FALSE); rqst->rq_xprt->xp_verf.oa_flavor = RPCSEC_GSS; rqst->rq_xprt->xp_verf.oa_base = checksum.value; rqst->rq_xprt->xp_verf.oa_length = checksum.length; } return (TRUE); }
DWORD VMCARESTVerifyKrbAuth( PVMCA_AUTHORIZATION_PARAM pAuthorization, PVMCA_ACCESS_TOKEN* ppAccessToken ) { DWORD dwError = 0; PSTR pszNegotiate = NULL; PSTR pszDecode = NULL; PSTR pszUser = NULL; char *pszToken = NULL; int nLength = 0; OM_uint32 major_status; OM_uint32 minor_status; gss_buffer_desc input_token = GSS_C_EMPTY_BUFFER; gss_buffer_desc output_token = GSS_C_EMPTY_BUFFER; gss_buffer_desc display_name = GSS_C_EMPTY_BUFFER; gss_ctx_id_t gss_context = GSS_C_NO_CONTEXT; gss_name_t client_name = GSS_C_NO_NAME; static gss_OID_desc gss_spnego_mech_oid_desc = {6, (void *)"\x2b\x06\x01\x05\x05\x02"}; static gss_OID gss_spnego_mech_oid = &gss_spnego_mech_oid_desc; gss_cred_id_t server_creds; pszNegotiate = pAuthorization->pszAuthorizationToken; if ( IsNullOrEmptyString(pszNegotiate) ) { dwError = EACCES; BAIL_ON_VMREST_ERROR(dwError); } if (!strcmp(pszNegotiate,"testing")) // TODO: REMOVE // TODO: DO NOT CHECK IN {// Kerberos backdoor for testing dwError = VMCARESTMakeKrbAccessToken(ppAccessToken); BAIL_ON_VMREST_ERROR(dwError); goto cleanup; } dwError = base64_decode( pszNegotiate, &pszDecode, &nLength ); BAIL_ON_VMREST_ERROR(dwError); dwError = server_acquire_creds( "HTTP", &gss_spnego_mech_oid_desc, &server_creds ); BAIL_ON_VMREST_ERROR(dwError); input_token.length = nLength; input_token.value = pszDecode; major_status = gss_accept_sec_context( &minor_status, &gss_context, server_creds, &input_token, GSS_C_NO_CHANNEL_BINDINGS, &client_name, &gss_spnego_mech_oid, &output_token, NULL, NULL, NULL ); if (GSS_ERROR(major_status) ) { //TODO: insert show error dwError = EACCES; BAIL_ON_VMREST_ERROR(dwError); } if (output_token.length) { dwError = make_negotiate_token(&output_token, &pszToken); BAIL_ON_VMREST_ERROR(dwError); } if (major_status == GSS_S_CONTINUE_NEEDED) { OM_uint32 min2; gss_buffer_desc mech_msg = GSS_C_EMPTY_BUFFER; gss_buffer_desc gss_msg = GSS_C_EMPTY_BUFFER; gss_buffer_desc minor_msg = GSS_C_EMPTY_BUFFER; OM_uint32 msg_ctx = 0; PSTR pszError = NULL; gss_oid_to_str(&min2, gss_spnego_mech_oid, &mech_msg); gss_display_status(&min2, major_status, GSS_C_GSS_CODE, gss_spnego_mech_oid, &msg_ctx, &gss_msg); gss_display_status(&min2, minor_status, GSS_C_MECH_CODE, gss_spnego_mech_oid, &msg_ctx, &minor_msg); VMCAAllocateStringPrintfA(&pszError, "gss_rc[%d:%*s] mech[%*s] minor[%u:%*s]", major_status, (int)gss_msg.length, (const char *)(gss_msg.value?gss_msg.value:""), (int)mech_msg.length, (const char *)(mech_msg.value?mech_msg.value:""), minor_status, (int)minor_msg.length, (const char *)(minor_msg.value?minor_msg.value:"")); gss_release_buffer(&min2, &mech_msg); gss_release_buffer(&min2, &gss_msg); gss_release_buffer(&min2, &minor_msg); } if (major_status == GSS_S_COMPLETE) { gss_display_name(&minor_status, client_name, &display_name, NULL); dwError = VMCAAllocateStringA(display_name.value, &pszUser); BAIL_ON_VMREST_ERROR(dwError); } dwError = VMCARESTMakeKrbAccessToken(ppAccessToken); BAIL_ON_VMREST_ERROR(dwError); cleanup: if (pszUser) { VMCA_SAFE_FREE_MEMORY(pszUser); } return dwError; error: goto cleanup; }
static int mag_auth(request_rec *req) { const char *type; struct mag_config *cfg; const char *auth_header; char *auth_header_type; char *auth_header_value; int ret = HTTP_UNAUTHORIZED; gss_ctx_id_t ctx = GSS_C_NO_CONTEXT; gss_ctx_id_t *pctx; gss_buffer_desc input = GSS_C_EMPTY_BUFFER; gss_buffer_desc output = GSS_C_EMPTY_BUFFER; gss_buffer_desc name = GSS_C_EMPTY_BUFFER; gss_name_t client = GSS_C_NO_NAME; gss_cred_id_t acquired_cred = GSS_C_NO_CREDENTIAL; gss_cred_id_t delegated_cred = GSS_C_NO_CREDENTIAL; uint32_t flags; uint32_t vtime; uint32_t maj, min; char *reply; size_t replen; char *clientname; gss_OID mech_type = GSS_C_NO_OID; gss_buffer_desc lname = GSS_C_EMPTY_BUFFER; struct mag_conn *mc = NULL; type = ap_auth_type(req); if ((type == NULL) || (strcasecmp(type, "GSSAPI") != 0)) { return DECLINED; } /* ignore auth for subrequests */ if (!ap_is_initial_req(req)) { return OK; } cfg = ap_get_module_config(req->per_dir_config, &auth_gssapi_module); if (cfg->ssl_only) { if (!mag_conn_is_https(req->connection)) { ap_log_rerror(APLOG_MARK, APLOG_ERR|APLOG_NOERRNO, 0, req, "Not a TLS connection, refusing to authenticate!"); goto done; } } if (cfg->gss_conn_ctx) { mc = (struct mag_conn *)ap_get_module_config( req->connection->conn_config, &auth_gssapi_module); if (!mc) { ap_log_rerror(APLOG_MARK, APLOG_DEBUG|APLOG_NOERRNO, 0, req, "Failed to retrieve connection context!"); goto done; } } /* if available, session always supersedes connection bound data */ mag_check_session(req, cfg, &mc); if (mc) { /* register the context in the memory pool, so it can be freed * when the connection/request is terminated */ apr_pool_userdata_set(mc, "mag_conn_ptr", mag_conn_destroy, mc->parent); if (mc->established) { ap_log_rerror(APLOG_MARK, APLOG_DEBUG|APLOG_NOERRNO, 0, req, "Already established context found!"); apr_table_set(req->subprocess_env, "GSS_NAME", mc->gss_name); req->ap_auth_type = apr_pstrdup(req->pool, "Negotiate"); req->user = apr_pstrdup(req->pool, mc->user_name); ret = OK; goto done; } pctx = &mc->ctx; } else { pctx = &ctx; } auth_header = apr_table_get(req->headers_in, "Authorization"); if (!auth_header) goto done; auth_header_type = ap_getword_white(req->pool, &auth_header); if (!auth_header_type) goto done; if (strcasecmp(auth_header_type, "Negotiate") != 0) goto done; auth_header_value = ap_getword_white(req->pool, &auth_header); if (!auth_header_value) goto done; input.length = apr_base64_decode_len(auth_header_value) + 1; input.value = apr_pcalloc(req->pool, input.length); if (!input.value) goto done; input.length = apr_base64_decode(input.value, auth_header_value); #ifdef HAVE_GSS_ACQUIRE_CRED_FROM if (cfg->use_s4u2proxy) { maj = gss_acquire_cred_from(&min, GSS_C_NO_NAME, 0, GSS_C_NO_OID_SET, GSS_C_BOTH, cfg->cred_store, &acquired_cred, NULL, NULL); if (GSS_ERROR(maj)) { ap_log_rerror(APLOG_MARK, APLOG_ERR|APLOG_NOERRNO, 0, req, "%s", mag_error(req, "gss_acquire_cred_from() failed", maj, min)); goto done; } } #endif maj = gss_accept_sec_context(&min, pctx, acquired_cred, &input, GSS_C_NO_CHANNEL_BINDINGS, &client, &mech_type, &output, &flags, &vtime, &delegated_cred); if (GSS_ERROR(maj)) { ap_log_rerror(APLOG_MARK, APLOG_ERR|APLOG_NOERRNO, 0, req, "%s", mag_error(req, "gss_accept_sec_context() failed", maj, min)); goto done; } if (maj == GSS_S_CONTINUE_NEEDED) { if (!mc) { ap_log_rerror(APLOG_MARK, APLOG_ERR|APLOG_NOERRNO, 0, req, "Mechanism needs continuation but neither " "GssapiConnectionBound nor " "GssapiUseSessions are available"); gss_delete_sec_context(&min, pctx, GSS_C_NO_BUFFER); gss_release_buffer(&min, &output); output.length = 0; } /* auth not complete send token and wait next packet */ goto done; } req->ap_auth_type = apr_pstrdup(req->pool, "Negotiate"); /* Always set the GSS name in an env var */ maj = gss_display_name(&min, client, &name, NULL); if (GSS_ERROR(maj)) { ap_log_rerror(APLOG_MARK, APLOG_ERR|APLOG_NOERRNO, 0, req, "%s", mag_error(req, "gss_accept_sec_context() failed", maj, min)); goto done; } clientname = apr_pstrndup(req->pool, name.value, name.length); apr_table_set(req->subprocess_env, "GSS_NAME", clientname); #ifdef HAVE_GSS_STORE_CRED_INTO if (cfg->deleg_ccache_dir && delegated_cred != GSS_C_NO_CREDENTIAL) { char *ccachefile = NULL; mag_store_deleg_creds(req, cfg->deleg_ccache_dir, clientname, delegated_cred, &ccachefile); if (ccachefile) { apr_table_set(req->subprocess_env, "KRB5CCNAME", ccachefile); } } #endif if (cfg->map_to_local) { maj = gss_localname(&min, client, mech_type, &lname); if (maj != GSS_S_COMPLETE) { ap_log_rerror(APLOG_MARK, APLOG_ERR|APLOG_NOERRNO, 0, req, "%s", mag_error(req, "gss_localname() failed", maj, min)); goto done; } req->user = apr_pstrndup(req->pool, lname.value, lname.length); } else { req->user = clientname; } if (mc) { mc->user_name = apr_pstrdup(mc->parent, req->user); mc->gss_name = apr_pstrdup(mc->parent, clientname); mc->established = true; if (vtime == GSS_C_INDEFINITE || vtime < MIN_SESS_EXP_TIME) { vtime = MIN_SESS_EXP_TIME; } mc->expiration = time(NULL) + vtime; mag_attempt_session(req, cfg, mc); } ret = OK; done: if (ret == HTTP_UNAUTHORIZED) { if (output.length != 0) { replen = apr_base64_encode_len(output.length) + 1; reply = apr_pcalloc(req->pool, 10 + replen); if (reply) { memcpy(reply, "Negotiate ", 10); apr_base64_encode(&reply[10], output.value, output.length); apr_table_add(req->err_headers_out, "WWW-Authenticate", reply); } } else { apr_table_add(req->err_headers_out, "WWW-Authenticate", "Negotiate"); } } gss_release_cred(&min, &delegated_cred); gss_release_buffer(&min, &output); gss_release_name(&min, &client); gss_release_buffer(&min, &name); gss_release_buffer(&min, &lname); return ret; }
enum auth_stat gssrpc__svcauth_gssapi( register struct svc_req *rqst, register struct rpc_msg *msg, bool_t *no_dispatch) { XDR xdrs; auth_gssapi_creds creds; auth_gssapi_init_arg call_arg; auth_gssapi_init_res call_res; gss_buffer_desc output_token, in_buf, out_buf; gss_cred_id_t server_creds; struct gss_channel_bindings_struct bindings, *bindp; OM_uint32 gssstat, minor_stat, time_rec; struct opaque_auth *cred, *verf; svc_auth_gssapi_data *client_data; int i; enum auth_stat ret; OM_uint32 ret_flags; uint32_t seq_num; PRINTF(("svcauth_gssapi: starting\n")); /* clean up expired entries */ clean_client(); /* use AUTH_NONE until there is a client_handle */ rqst->rq_xprt->xp_auth = &svc_auth_none; memset((char *) &call_res, 0, sizeof(call_res)); creds.client_handle.length = 0; creds.client_handle.value = NULL; cred = &msg->rm_call.cb_cred; verf = &msg->rm_call.cb_verf; if (cred->oa_length == 0) { PRINTF(("svcauth_gssapi: empty creds, failing\n")); LOG_MISCERR("empty client credentials"); ret = AUTH_BADCRED; goto error; } PRINTF(("svcauth_gssapi: decoding credentials\n")); xdrmem_create(&xdrs, cred->oa_base, cred->oa_length, XDR_DECODE); memset((char *) &creds, 0, sizeof(creds)); if (! xdr_authgssapi_creds(&xdrs, &creds)) { PRINTF(("svcauth_gssapi: failed decoding creds\n")); LOG_MISCERR("protocol error in client credentials"); xdr_free(xdr_authgssapi_creds, &creds); XDR_DESTROY(&xdrs); ret = AUTH_BADCRED; goto error; } XDR_DESTROY(&xdrs); PRINTF(("svcauth_gssapi: got credentials, version %d, client_handle len %d\n", creds.version, (int) creds.client_handle.length)); if (creds.version != 2) { PRINTF(("svcauth_gssapi: bad credential version\n")); LOG_MISCERR("unsupported client credentials version"); ret = AUTH_BADCRED; goto error; } #ifdef DEBUG_GSSAPI if (svc_debug_gssapi) { if (creds.auth_msg && rqst->rq_proc == AUTH_GSSAPI_EXIT) { PRINTF(("svcauth_gssapi: GSSAPI_EXIT, cleaning up\n")); svc_sendreply(rqst->rq_xprt, xdr_void, NULL); xdr_free(xdr_authgssapi_creds, &creds); cleanup(); exit(0); } } #endif /* * If this is an auth_msg and proc is GSSAPI_INIT, then create a * client handle for this client. Otherwise, look up the * existing handle. */ if (creds.auth_msg && rqst->rq_proc == AUTH_GSSAPI_INIT) { if (creds.client_handle.length != 0) { PRINTF(("svcauth_gssapi: non-empty handle on GSSAPI_INIT\n")); LOG_MISCERR("protocol error in client handle"); ret = AUTH_FAILED; goto error; } PRINTF(("svcauth_gssapi: GSSAPI_INIT, creating client.\n")); client_data = create_client(); if (client_data == NULL) { PRINTF(("svcauth_gssapi: create_client failed\n")); LOG_MISCERR("internal error creating client record"); ret = AUTH_FAILED; goto error; } } else { if (creds.client_handle.length == 0) { PRINTF(("svcauth_gssapi: expected non-empty creds\n")); LOG_MISCERR("protocol error in client credentials"); ret = AUTH_FAILED; goto error; } PRINTF(("svcauth_gssapi: incoming client_handle %d, len %d\n", *((uint32_t *) creds.client_handle.value), (int) creds.client_handle.length)); client_data = get_client(&creds.client_handle); if (client_data == NULL) { PRINTF(("svcauth_gssapi: client_handle lookup failed\n")); LOG_MISCERR("invalid client handle received"); ret = AUTH_BADCRED; goto error; } PRINTF(("svcauth_gssapi: client_handle lookup succeeded\n")); } /* any response we send will use client_handle, so set it now */ call_res.client_handle.length = sizeof(client_data->key); call_res.client_handle.value = (char *) &client_data->key; /* mark this call as using AUTH_GSSAPI via client_data's SVCAUTH */ rqst->rq_xprt->xp_auth = &client_data->svcauth; if (client_data->established == FALSE) { PRINTF(("svcauth_gssapi: context is not established\n")); if (creds.auth_msg == FALSE) { PRINTF(("svcauth_gssapi: expected auth_msg TRUE\n")); LOG_MISCERR("protocol error on incomplete connection"); ret = AUTH_REJECTEDCRED; goto error; } /* * If the context is not established, then only GSSAPI_INIT * and _CONTINUE requests are valid. */ if (rqst->rq_proc != AUTH_GSSAPI_INIT && rqst->rq_proc != AUTH_GSSAPI_CONTINUE_INIT) { PRINTF(("svcauth_gssapi: unacceptable procedure %d\n", rqst->rq_proc)); LOG_MISCERR("protocol error on incomplete connection"); ret = AUTH_FAILED; goto error; } /* call is for us, deserialize arguments */ memset(&call_arg, 0, sizeof(call_arg)); if (! svc_getargs(rqst->rq_xprt, xdr_authgssapi_init_arg, &call_arg)) { PRINTF(("svcauth_gssapi: cannot decode args\n")); LOG_MISCERR("protocol error in procedure arguments"); ret = AUTH_BADCRED; goto error; } /* * Process the call arg version number. * * Set the krb5_gss backwards-compatibility mode based on client * version. This controls whether the AP_REP message is * encrypted with the session key (version 2+, correct) or the * session subkey (version 1, incorrect). This function can * never fail, so we don't bother checking its return value. */ switch (call_arg.version) { case 1: case 2: LOG_MISCERR("Warning: Accepted old RPC protocol request"); call_res.version = 1; break; case 3: case 4: /* 3 and 4 are essentially the same, don't bother warning */ call_res.version = call_arg.version; break; default: PRINTF(("svcauth_gssapi: bad GSSAPI_INIT version\n")); LOG_MISCERR("unsupported GSSAPI_INIT version"); ret = AUTH_BADCRED; goto error; } #ifdef GSS_BACKWARD_HACK krb5_gss_set_backward_mode(&minor_stat, call_arg.version == 1); #endif if (call_arg.version >= 3) { memset(&bindings, 0, sizeof(bindings)); bindings.application_data.length = 0; bindings.initiator_addrtype = GSS_C_AF_INET; bindings.initiator_address.length = 4; bindings.initiator_address.value = &svc_getcaller(rqst->rq_xprt)->sin_addr.s_addr; if (rqst->rq_xprt->xp_laddrlen > 0) { bindings.acceptor_addrtype = GSS_C_AF_INET; bindings.acceptor_address.length = 4; bindings.acceptor_address.value = &rqst->rq_xprt->xp_laddr.sin_addr.s_addr; } else { LOG_MISCERR("cannot get local address"); ret = AUTH_FAILED; goto error; } bindp = &bindings; } else { bindp = GSS_C_NO_CHANNEL_BINDINGS; } /* * If the client's server_creds is already set, use it. * Otherwise, try each credential in server_creds_list until * one of them succeedes, then set the client server_creds * to that. If all fail, the client's server_creds isn't * set (which is fine, because the client will be gc'ed * anyway). * * If accept_sec_context returns something other than * success and GSS_S_FAILURE, then assume different * credentials won't help and stop looping. * * Note that there are really two cases here: (1) the client * has a server_creds already, and (2) it does not. They * are both written in the same loop so that there is only * one textual call to gss_accept_sec_context; in fact, in * case (1), the loop is executed exactly once. */ for (i = 0; i < server_creds_count; i++) { if (client_data->server_creds != NULL) { PRINTF(("svcauth_gssapi: using's clients server_creds\n")); server_creds = client_data->server_creds; } else { PRINTF(("svcauth_gssapi: trying creds %d\n", i)); server_creds = server_creds_list[i]; } /* Free previous output_token from loop */ if(i != 0) gss_release_buffer(&minor_stat, &output_token); call_res.gss_major = gss_accept_sec_context(&call_res.gss_minor, &client_data->context, server_creds, &call_arg.token, bindp, &client_data->client_name, NULL, &output_token, &ret_flags, &time_rec, NULL); if (server_creds == client_data->server_creds) break; PRINTF(("accept_sec_context returned 0x%x 0x%x wrong-princ=%#x\n", call_res.gss_major, call_res.gss_minor, (int) KRB5KRB_AP_WRONG_PRINC)); if (call_res.gss_major == GSS_S_COMPLETE || call_res.gss_major == GSS_S_CONTINUE_NEEDED) { /* server_creds was right, set it! */ PRINTF(("svcauth_gssapi: creds are correct, storing\n")); client_data->server_creds = server_creds; client_data->server_name = server_name_list[i]; break; } else if (call_res.gss_major != GSS_S_FAILURE #ifdef GSSAPI_KRB5 /* * hard-coded because there is no other way * to prevent all GSS_S_FAILURES from * returning a "wrong principal in request" * error */ || ((krb5_error_code) call_res.gss_minor != (krb5_error_code) KRB5KRB_AP_WRONG_PRINC) #endif ) { break; } } gssstat = call_res.gss_major; minor_stat = call_res.gss_minor; /* done with call args */ xdr_free(xdr_authgssapi_init_arg, &call_arg); PRINTF(("svcauth_gssapi: accept_sec_context returned %#x %#x\n", call_res.gss_major, call_res.gss_minor)); if (call_res.gss_major != GSS_S_COMPLETE && call_res.gss_major != GSS_S_CONTINUE_NEEDED) { AUTH_GSSAPI_DISPLAY_STATUS(("accepting context", call_res.gss_major, call_res.gss_minor)); if (log_badauth != NULL) (*log_badauth)(call_res.gss_major, call_res.gss_minor, &rqst->rq_xprt->xp_raddr, log_badauth_data); gss_release_buffer(&minor_stat, &output_token); svc_sendreply(rqst->rq_xprt, xdr_authgssapi_init_res, (caddr_t) &call_res); *no_dispatch = TRUE; ret = AUTH_OK; goto error; } if (output_token.length != 0) { PRINTF(("svcauth_gssapi: got new output token\n")); GSS_COPY_BUFFER(call_res.token, output_token); } if (gssstat == GSS_S_COMPLETE) { client_data->seq_num = rand(); client_expire(client_data, (time_rec == GSS_C_INDEFINITE ? INDEF_EXPIRE : time_rec) + time(0)); PRINTF(("svcauth_gssapi: context established, isn %d\n", client_data->seq_num)); if (auth_gssapi_seal_seq(client_data->context, client_data->seq_num, &call_res.signed_isn) == FALSE) { ret = AUTH_FAILED; LOG_MISCERR("internal error sealing sequence number"); gss_release_buffer(&minor_stat, &output_token); goto error; } } PRINTF(("svcauth_gssapi: sending reply\n")); svc_sendreply(rqst->rq_xprt, xdr_authgssapi_init_res, (caddr_t) &call_res); *no_dispatch = TRUE; /* * If appropriate, set established to TRUE *after* sending * response (otherwise, the client will receive the final * token encrypted) */ if (gssstat == GSS_S_COMPLETE) { gss_release_buffer(&minor_stat, &call_res.signed_isn); client_data->established = TRUE; } gss_release_buffer(&minor_stat, &output_token); } else { PRINTF(("svcauth_gssapi: context is established\n")); /* check the verifier */ PRINTF(("svcauth_gssapi: checking verifier, len %d\n", verf->oa_length)); in_buf.length = verf->oa_length; in_buf.value = verf->oa_base; if (auth_gssapi_unseal_seq(client_data->context, &in_buf, &seq_num) == FALSE) { ret = AUTH_BADVERF; LOG_MISCERR("internal error unsealing sequence number"); goto error; } if (seq_num != client_data->seq_num + 1) { PRINTF(("svcauth_gssapi: expected isn %d, got %d\n", client_data->seq_num + 1, seq_num)); if (log_badverf != NULL) (*log_badverf)(client_data->client_name, client_data->server_name, rqst, msg, log_badverf_data); ret = AUTH_REJECTEDVERF; goto error; } client_data->seq_num++; PRINTF(("svcauth_gssapi: seq_num %d okay\n", seq_num)); /* free previous response verifier, if any */ if (client_data->prev_verf.length != 0) { gss_release_buffer(&minor_stat, &client_data->prev_verf); client_data->prev_verf.length = 0; } /* prepare response verifier */ seq_num = client_data->seq_num + 1; if (auth_gssapi_seal_seq(client_data->context, seq_num, &out_buf) == FALSE) { ret = AUTH_FAILED; LOG_MISCERR("internal error sealing sequence number"); goto error; } client_data->seq_num++; PRINTF(("svcauth_gssapi; response seq_num %d\n", seq_num)); rqst->rq_xprt->xp_verf.oa_flavor = AUTH_GSSAPI; rqst->rq_xprt->xp_verf.oa_base = out_buf.value; rqst->rq_xprt->xp_verf.oa_length = out_buf.length; /* save verifier so it can be freed next time */ client_data->prev_verf.value = out_buf.value; client_data->prev_verf.length = out_buf.length; /* * Message is authentic. If auth_msg if true, process the * call; otherwise, return AUTH_OK so it will be dispatched * to the application server. */ if (creds.auth_msg == TRUE) { /* * If process_token fails, then the token probably came * from an attacker. No response (error or otherwise) * should be returned to the client, since it won't be * accepting one. */ switch (rqst->rq_proc) { case AUTH_GSSAPI_MSG: PRINTF(("svcauth_gssapi: GSSAPI_MSG, getting args\n")); memset(&call_arg, 0, sizeof(call_arg)); if (! svc_getargs(rqst->rq_xprt, xdr_authgssapi_init_arg, &call_arg)) { PRINTF(("svcauth_gssapi: cannot decode args\n")); LOG_MISCERR("protocol error in call arguments"); xdr_free(xdr_authgssapi_init_arg, &call_arg); ret = AUTH_BADCRED; goto error; } PRINTF(("svcauth_gssapi: processing token\n")); gssstat = gss_process_context_token(&minor_stat, client_data->context, &call_arg.token); /* done with call args */ xdr_free(xdr_authgssapi_init_arg, &call_arg); if (gssstat != GSS_S_COMPLETE) { AUTH_GSSAPI_DISPLAY_STATUS(("processing token", gssstat, minor_stat)); ret = AUTH_FAILED; goto error; } svc_sendreply(rqst->rq_xprt, xdr_void, NULL); *no_dispatch = TRUE; break; case AUTH_GSSAPI_DESTROY: PRINTF(("svcauth_gssapi: GSSAPI_DESTROY\n")); PRINTF(("svcauth_gssapi: sending reply\n")); svc_sendreply(rqst->rq_xprt, xdr_void, NULL); *no_dispatch = TRUE; destroy_client(client_data); rqst->rq_xprt->xp_auth = NULL; break; default: PRINTF(("svcauth_gssapi: unacceptable procedure %d\n", rqst->rq_proc)); LOG_MISCERR("invalid call procedure number"); ret = AUTH_FAILED; goto error; } } else { /* set credentials for app server; comment in svc.c */ /* seems to imply this is incorrect, but I don't see */ /* any problem with it... */ rqst->rq_clntcred = (char *)client_data->client_name; rqst->rq_svccred = (char *)client_data->context; } } if (creds.client_handle.length != 0) { PRINTF(("svcauth_gssapi: freeing client_handle len %d\n", (int) creds.client_handle.length)); xdr_free(xdr_authgssapi_creds, &creds); } PRINTF(("\n")); return AUTH_OK; error: if (creds.client_handle.length != 0) { PRINTF(("svcauth_gssapi: freeing client_handle len %d\n", (int) creds.client_handle.length)); xdr_free(xdr_authgssapi_creds, &creds); } PRINTF(("\n")); return ret; }
int authenticate_gss_server_step(gss_server_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; // 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) { size_t len; input_token.value = base64_decode(challenge, &len); input_token.length = len; } else { PyErr_SetString(KrbException_class, "No challenge parameter in request from client"); ret = AUTH_GSS_ERROR; goto end; } Py_BEGIN_ALLOW_THREADS 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, (OM_uint32 *)&state->gss_flags, NULL, &state->client_creds); Py_END_ALLOW_THREADS if (GSS_ERROR(maj_stat)) { set_gss_error(maj_stat, min_stat); ret = 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)) { set_gss_error(maj_stat, min_stat); ret = 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)) { set_gss_error(maj_stat, min_stat); ret = AUTH_GSS_ERROR; goto end; } maj_stat = gss_display_name(&min_stat, target_name, &output_token, NULL); if (GSS_ERROR(maj_stat)) { set_gss_error(maj_stat, min_stat); ret = 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; } ret = AUTH_GSS_COMPLETE; end: if (output_token.length) gss_release_buffer(&min_stat, &output_token); if (input_token.value) free(input_token.value); return ret; }
static int accept_user(gss_ctx_id_t *ctx, gss_buffer_desc *in, gss_buffer_desc *out, gss_buffer_desc *name, char **pccname) { OM_uint32 maj_stat, min_stat; gss_name_t src_name = GSS_C_NO_NAME; gss_OID oid = GSS_C_NO_OID; int ret = -1; gss_cred_id_t delegated_cred_handle = NULL; *pccname = NULL; maj_stat = gss_accept_sec_context(&min_stat, ctx, GSS_C_NO_CREDENTIAL, in, GSS_C_NO_CHANNEL_BINDINGS, &src_name, &oid, out, NULL, NULL, &delegated_cred_handle); if ((maj_stat & GSS_S_CONTINUE_NEEDED) || maj_stat != GSS_S_COMPLETE) { fprintf(stderr, "gss_accept_sec_context: %08x %d %d ", maj_stat, maj_stat & GSS_S_CONTINUE_NEEDED, maj_stat != GSS_S_COMPLETE); gss_print_errors(min_stat); ret = HTTP_UNAUTHORIZED; goto out; } if (name) { /* Use display name */ maj_stat = gss_display_name(&min_stat, src_name, name, NULL); if (maj_stat != GSS_S_COMPLETE) { ret = HTTP_UNAUTHORIZED; goto out; } } ret = OK; if (delegated_cred_handle) { const struct mech_specific *m; m = find_mech(oid); if (m && m->save_cred) (*m->save_cred)(name->value, delegated_cred_handle, pccname); } else { /* fprintf(stderr, "Not delegated\r\n"); */ } out: if (src_name != GSS_C_NO_NAME) gss_release_name(&min_stat, &src_name); return ret; }
int main (int argc, char *argv[]) { gss_uint32 maj_stat, min_stat, ret_flags, time_rec; gss_buffer_desc bufdesc, bufdesc2; gss_name_t servername = GSS_C_NO_NAME, name; gss_ctx_id_t cctx = GSS_C_NO_CONTEXT; gss_ctx_id_t sctx = GSS_C_NO_CONTEXT; gss_cred_id_t server_creds; Shishi *handle; size_t i; struct gss_channel_bindings_struct cb; memset (&cb, 0, sizeof (cb)); cb.application_data.length = 3; cb.application_data.value = (char *) "hej"; do if (strcmp (argv[argc - 1], "-v") == 0 || strcmp (argv[argc - 1], "--verbose") == 0) debug = 1; else if (strcmp (argv[argc - 1], "-b") == 0 || strcmp (argv[argc - 1], "--break-on-error") == 0) break_on_error = 1; else if (strcmp (argv[argc - 1], "-h") == 0 || strcmp (argv[argc - 1], "-?") == 0 || strcmp (argv[argc - 1], "--help") == 0) { printf ("Usage: %s [-vbh?] [--verbose] [--break-on-error] [--help]\n", argv[0]); return 1; } while (argc-- > 1); handle = shishi (); /* Name of service. */ bufdesc.value = (char *) "*****@*****.**"; bufdesc.length = strlen (bufdesc.value); maj_stat = gss_import_name (&min_stat, &bufdesc, GSS_C_NT_HOSTBASED_SERVICE, &servername); if (GSS_ERROR (maj_stat)) fail ("gss_import_name (host/server)\n"); /* Get credential, for server. */ maj_stat = gss_acquire_cred (&min_stat, servername, 0, GSS_C_NULL_OID_SET, GSS_C_ACCEPT, &server_creds, NULL, NULL); if (GSS_ERROR (maj_stat)) { fail ("gss_acquire_cred"); display_status ("acquire credentials", maj_stat, min_stat); } for (i = 0; i < 3; i++) { /* Start client. */ switch (i) { case 0: maj_stat = gss_init_sec_context (&min_stat, GSS_C_NO_CREDENTIAL, &cctx, servername, GSS_KRB5, GSS_C_MUTUAL_FLAG | GSS_C_REPLAY_FLAG | GSS_C_SEQUENCE_FLAG, 0, GSS_C_NO_CHANNEL_BINDINGS, GSS_C_NO_BUFFER, NULL, &bufdesc2, NULL, NULL); if (maj_stat != GSS_S_CONTINUE_NEEDED) fail ("loop 0 init failure\n"); break; case 1: /* Default OID, channel bindings. */ maj_stat = gss_init_sec_context (&min_stat, GSS_C_NO_CREDENTIAL, &cctx, servername, GSS_C_NO_OID, GSS_C_MUTUAL_FLAG | GSS_C_REPLAY_FLAG | GSS_C_SEQUENCE_FLAG, 0, &cb, GSS_C_NO_BUFFER, NULL, &bufdesc2, NULL, NULL); if (maj_stat != GSS_S_CONTINUE_NEEDED) fail ("loop 0 init failure\n"); break; case 2: /* No mutual authentication. */ maj_stat = gss_init_sec_context (&min_stat, GSS_C_NO_CREDENTIAL, &cctx, servername, GSS_KRB5, GSS_C_REPLAY_FLAG | GSS_C_CONF_FLAG | GSS_C_SEQUENCE_FLAG, 0, GSS_C_NO_CHANNEL_BINDINGS, GSS_C_NO_BUFFER, NULL, &bufdesc2, &ret_flags, NULL); if (ret_flags != (GSS_C_REPLAY_FLAG | GSS_C_CONF_FLAG | GSS_C_SEQUENCE_FLAG | GSS_C_PROT_READY_FLAG)) fail ("loop 2 ret_flags failure (%d)\n", ret_flags); if (maj_stat != GSS_S_COMPLETE) fail ("loop 1 init failure\n"); break; default: fail ("default?!\n"); break; } if (GSS_ERROR (maj_stat)) { fail ("gss_accept_sec_context failure\n"); display_status ("accept_sec_context", maj_stat, min_stat); } if (debug) { char *p = bufdesc2.value; Shishi_asn1 apreq = shishi_der2asn1_apreq (handle, p + 17, bufdesc2.length - 17); printf ("\nClient AP-REQ:\n\n"); shishi_apreq_print (handle, stdout, apreq); } /* Start server. */ switch (i) { case 0: maj_stat = gss_accept_sec_context (&min_stat, &sctx, server_creds, &bufdesc2, GSS_C_NO_CHANNEL_BINDINGS, &name, NULL, &bufdesc, &ret_flags, &time_rec, NULL); if (ret_flags != (GSS_C_MUTUAL_FLAG | /* XXX GSS_C_REPLAY_FLAG | GSS_C_SEQUENCE_FLAG | */ GSS_C_PROT_READY_FLAG)) fail ("loop 0 accept flag failure (%d)\n", ret_flags); break; case 1: maj_stat = gss_accept_sec_context (&min_stat, &sctx, server_creds, &bufdesc2, &cb, &name, NULL, &bufdesc, &ret_flags, &time_rec, NULL); break; case 2: maj_stat = gss_accept_sec_context (&min_stat, &sctx, server_creds, &bufdesc2, GSS_C_NO_CHANNEL_BINDINGS, &name, NULL, &bufdesc, &ret_flags, &time_rec, NULL); break; default: fail ("default?!\n"); break; } if (GSS_ERROR (maj_stat)) { fail ("gss_accept_sec_context failure\n"); display_status ("accept_sec_context", maj_stat, min_stat); } if (debug) { char *p = bufdesc2.value; Shishi_asn1 aprep = shishi_der2asn1_aprep (handle, p + 15, bufdesc.length - 15); printf ("\nServer AP-REP:\n\n"); shishi_aprep_print (handle, stdout, aprep); } switch (i) { case 0: maj_stat = gss_init_sec_context (&min_stat, GSS_C_NO_CREDENTIAL, &cctx, servername, GSS_KRB5, GSS_C_MUTUAL_FLAG | GSS_C_REPLAY_FLAG | GSS_C_SEQUENCE_FLAG, 0, GSS_C_NO_CHANNEL_BINDINGS, &bufdesc, NULL, &bufdesc2, NULL, NULL); break; case 1: /* Check ret_flags. */ maj_stat = gss_init_sec_context (&min_stat, GSS_C_NO_CREDENTIAL, &cctx, servername, GSS_KRB5, GSS_C_MUTUAL_FLAG | GSS_C_REPLAY_FLAG | GSS_C_SEQUENCE_FLAG, 0, GSS_C_NO_CHANNEL_BINDINGS, &bufdesc, NULL, &bufdesc2, &ret_flags, &time_rec); if (ret_flags != (GSS_C_MUTUAL_FLAG | GSS_C_REPLAY_FLAG | GSS_C_SEQUENCE_FLAG | GSS_C_PROT_READY_FLAG)) fail ("loop 1 ret_flags failure (%d)\n", ret_flags); break; /* No case 2. */ default: break; } if (GSS_ERROR (maj_stat)) { fail ("gss_init_sec_context failure (2)\n"); display_status ("init_sec_context", maj_stat, min_stat); } { gss_buffer_desc pt, pt2, ct; int conf_state; gss_qop_t qop_state; pt.value = (char *) "foo"; pt.length = strlen (pt.value) + 1; maj_stat = gss_wrap (&min_stat, cctx, 0, 0, &pt, &conf_state, &ct); if (GSS_ERROR (maj_stat)) { fail ("client gss_wrap failure\n"); display_status ("client wrap", maj_stat, min_stat); } maj_stat = gss_unwrap (&min_stat, sctx, &ct, &pt2, &conf_state, &qop_state); if (GSS_ERROR (maj_stat)) { fail ("server gss_unwrap failure\n"); display_status ("client wrap", maj_stat, min_stat); } if (pt.length != pt2.length || memcmp (pt2.value, pt.value, pt.length) != 0) fail ("wrap+unwrap failed (%d, %d, %.*s)\n", (int) pt.length, (int) pt2.length, (int) pt2.length, (char *) pt2.value); gss_release_buffer (&min_stat, &ct); gss_release_buffer (&min_stat, &pt2); } maj_stat = gss_delete_sec_context (&min_stat, &cctx, GSS_C_NO_BUFFER); if (GSS_ERROR (maj_stat)) { fail ("client gss_delete_sec_context failure\n"); display_status ("client delete_sec_context", maj_stat, min_stat); } maj_stat = gss_delete_sec_context (&min_stat, &sctx, GSS_C_NO_BUFFER); if (GSS_ERROR (maj_stat)) { fail ("server gss_delete_sec_context failure\n"); display_status ("server delete_sec_context", maj_stat, min_stat); } success ("loop %d ok\n", (int) i); } /* Clean up. */ maj_stat = gss_release_cred (&min_stat, &server_creds); if (GSS_ERROR (maj_stat)) { fail ("gss_release_cred"); display_status ("release credentials", maj_stat, min_stat); } maj_stat = gss_release_name (&min_stat, &servername); if (GSS_ERROR (maj_stat)) { fail ("gss_release_name failure\n"); display_status ("gss_release_name", maj_stat, min_stat); } shishi_done (handle); /* We're done. */ if (debug) printf ("Kerberos 5 security context self tests done with %d errors\n", error_count); return error_count ? 1 : 0; }
void * example_gss_accept( int socket, struct sockaddr *addr, socklen_t *addr_len, int *sock_out) { example_gss_state_t *state; int rc; gss_buffer_desc inbuf, token; OM_uint32 maj_stat, min_stat; state = malloc(sizeof(example_gss_state_t)); state->socket = accept(socket, addr, addr_len); if (state->socket < 0) { rc = -1; goto err; } *sock_out = state->socket; state->ctx = GSS_C_NO_CONTEXT; state->buffer.value = NULL; state->buffer.length = 0; inbuf.value = malloc(1024); do { inbuf.length = 1024; /* Read token */ rc = recv(state->socket, inbuf.value, inbuf.length, 0); if (rc < 0) { break; } inbuf.length = rc; /* Pass to accept sec context */ maj_stat = gss_accept_sec_context( &min_stat, &state->ctx, GSS_C_NO_CREDENTIAL, &inbuf, GSS_C_NO_CHANNEL_BINDINGS, NULL, NULL, &token, NULL, NULL, NULL); if (GSS_ERROR(maj_stat)) { break; } /* Send output token if available */ if (token.length > 0) { int sent; for (sent = 0; sent < token.length; ) { rc = send(state->socket, ((char *) token.value)+sent, token.length-sent, 0); if (rc < 0) { break; } else { sent += rc; } } gss_release_buffer(&min_stat, &token); token.value = NULL; token.length = 0; } } while (maj_stat == GSS_S_CONTINUE_NEEDED); if (inbuf.value) { free(inbuf.value); } if (GSS_ERROR(maj_stat)) { rc = -1; } err: if (rc < 0) { free(state); state = NULL; } return state; }
static int pamGssInitAcceptSecContext(pam_handle_t *pamh, int confFlags, gss_cred_id_t cred, gss_cred_id_t acceptorCred, gss_name_t hostName, gss_OID mech) { int status; OM_uint32 major, minor; gss_buffer_desc initiatorToken = GSS_C_EMPTY_BUFFER; gss_buffer_desc acceptorToken = GSS_C_EMPTY_BUFFER; gss_ctx_id_t initiatorContext = GSS_C_NO_CONTEXT; gss_ctx_id_t acceptorContext = GSS_C_NO_CONTEXT; gss_buffer_desc canonUserNameBuf = GSS_C_EMPTY_BUFFER; gss_name_t canonUserName = GSS_C_NO_NAME; gss_OID canonMech = GSS_C_NO_OID; OM_uint32 gssFlags; do { major = gss_init_sec_context(&minor, cred, &initiatorContext, hostName, mech, GSS_C_MUTUAL_FLAG, GSS_C_INDEFINITE, GSS_C_NO_CHANNEL_BINDINGS, &acceptorToken, NULL, &initiatorToken, &gssFlags, NULL); gss_release_buffer(&minor, &acceptorToken); #ifdef GSS_S_PROMPTING_NEEDED if (major == GSS_S_PROMPTING_NEEDED) { status = PAM_CRED_INSUFFICIENT; goto cleanup; } #endif BAIL_ON_GSS_ERROR(major, minor); if (initiatorToken.length != 0) { major = gss_accept_sec_context(&minor, &acceptorContext, acceptorCred, &initiatorToken, GSS_C_NO_CHANNEL_BINDINGS, &canonUserName, &canonMech, &acceptorToken, NULL, NULL, NULL); gss_release_buffer(&minor, &initiatorToken); } BAIL_ON_GSS_ERROR(major, minor); } while (major == GSS_S_CONTINUE_NEEDED); BAIL_ON_GSS_ERROR(major, minor); if ((gssFlags & GSS_C_MUTUAL_FLAG) == 0) { status = PAM_PERM_DENIED; goto cleanup; } #ifndef __APPLE__ major = gss_localname(&minor, canonUserName, GSS_C_NO_OID, &canonUserNameBuf); if (major == GSS_S_COMPLETE) { status = pam_set_item(pamh, PAM_USER, canonUserNameBuf.value); BAIL_ON_PAM_ERROR(status); } else if (major != GSS_S_UNAVAILABLE) goto cleanup; #endif status = pam_set_data(pamh, GSS_NAME_DATA, canonUserName, pamGssCleanupName); BAIL_ON_PAM_ERROR(status); canonUserName = GSS_C_NO_NAME; status = pam_set_data(pamh, GSS_MECH_DATA, canonMech, pamGssCleanupMech); BAIL_ON_PAM_ERROR(status); canonMech = GSS_C_NO_OID; status = PAM_SUCCESS; cleanup: gss_release_name(&minor, &canonUserName); gss_release_buffer(&minor, &initiatorToken); gss_release_buffer(&minor, &acceptorToken); gss_delete_sec_context(&minor, &initiatorContext, NULL); gss_delete_sec_context(&minor, &acceptorContext, NULL); gss_release_buffer(&minor, &canonUserNameBuf); if (IGNORE_ERR_P(status, confFlags)) status = PAM_IGNORE; return status; }
int main(int argc, char * const argv[]) { char buf[MAX_AUTHTOKEN_LEN]; char *c; char *user=NULL; int length=0; static int err=0; int opt, rc, debug=0, loging=0; OM_uint32 ret_flags=0, spnego_flag=0; char *service_name=(char *)"HTTP",*host_name=NULL; char *token = NULL; char *service_principal = NULL; OM_uint32 major_status, minor_status; gss_ctx_id_t gss_context = GSS_C_NO_CONTEXT; gss_name_t client_name = GSS_C_NO_NAME; gss_name_t server_name = GSS_C_NO_NAME; gss_cred_id_t server_creds = GSS_C_NO_CREDENTIAL; gss_cred_id_t delegated_cred = GSS_C_NO_CREDENTIAL; gss_buffer_desc service = GSS_C_EMPTY_BUFFER; gss_buffer_desc input_token = GSS_C_EMPTY_BUFFER; gss_buffer_desc output_token = GSS_C_EMPTY_BUFFER; const unsigned char *kerberosToken = NULL; size_t kerberosTokenLength = 0; const unsigned char *spnegoToken = NULL ; size_t spnegoTokenLength = 0; setbuf(stdout,NULL); setbuf(stdin,NULL); while (-1 != (opt = getopt(argc, argv, "dis:h"))) { switch (opt) { case 'd': debug = 1; break; case 'i': loging = 1; break; case 's': service_principal = strdup(optarg); break; case 'h': fprintf(stdout, "Usage: \n"); fprintf(stdout, "squid_kerb_auth -d [-s SPN]\n"); fprintf(stdout, "SPN = service principal name\n"); fprintf(stdout, "Can be set to GSS_C_NO_NAME to allow any entry from keytab\n"); fprintf(stdout, "default SPN is HTTP/fqdn@DEFAULT_REALM\n"); break; default: fprintf(stderr, "%s| %s: unknown option: -%c.\n", LogTime(), PROGRAM, opt); } } if (service_principal && strcasecmp(service_principal,"GSS_C_NO_NAME") ) { service.value = service_principal; service.length = strlen((char *)service.value); } else { host_name=gethost_name(); if ( !host_name ) { fprintf(stderr, "%s| %s: Local hostname could not be determined. Please specify the service principal\n", LogTime(), PROGRAM); exit(-1); } service.value = malloc(strlen(service_name)+strlen(host_name)+2); snprintf(service.value,strlen(service_name)+strlen(host_name)+2,"%s@%s",service_name,host_name); service.length = strlen((char *)service.value); } while (1) { if (fgets(buf, sizeof(buf)-1, stdin) == NULL) { if (ferror(stdin)) { if (debug) fprintf(stderr, "%s| %s: fgets() failed! dying..... errno=%d (%s)\n", LogTime(), PROGRAM, ferror(stdin), strerror(ferror(stdin))); exit(1); /* BIIG buffer */ } exit(0); } c=memchr(buf,'\n',sizeof(buf)-1); if (c) { *c = '\0'; length = c-buf; } else { err = 1; } if (err) { if (debug) fprintf(stderr, "%s| %s: Oversized message\n", LogTime(), PROGRAM); fprintf(stdout, "NA Oversized message\n"); err = 0; continue; } if (debug) fprintf(stderr, "%s| %s: Got '%s' from squid (length: %d).\n", LogTime(), PROGRAM, buf ,length); if (buf[0] == '\0') { if (debug) fprintf(stderr, "%s| %s: Invalid request\n", LogTime(), PROGRAM); fprintf(stdout, "NA Invalid request\n"); continue; } if (strlen(buf) < 2) { if (debug) fprintf(stderr, "%s| %s: Invalid request [%s]\n", LogTime(), PROGRAM, buf); fprintf(stdout, "NA Invalid request\n"); continue; } if ( !strncmp(buf, "QQ", 2) ) { gss_release_buffer(&minor_status, &input_token); gss_release_buffer(&minor_status, &output_token); gss_release_buffer(&minor_status, &service); gss_release_cred(&minor_status, &server_creds); gss_release_cred(&minor_status, &delegated_cred); gss_release_name(&minor_status, &server_name); gss_release_name(&minor_status, &client_name); gss_delete_sec_context(&minor_status, &gss_context, NULL); if (kerberosToken) { /* Allocated by parseNegTokenInit, but no matching free function exists.. */ if (!spnego_flag) free((char *)kerberosToken); kerberosToken=NULL; } if (spnego_flag) { /* Allocated by makeNegTokenTarg, but no matching free function exists.. */ if (spnegoToken) free((char *)spnegoToken); spnegoToken=NULL; } if (token) { free(token); token=NULL; } if (host_name) { free(host_name); host_name=NULL; } exit(0); } if ( !strncmp(buf, "YR", 2) && !strncmp(buf, "KK", 2) ) { if (debug) fprintf(stderr, "%s| %s: Invalid request [%s]\n", LogTime(), PROGRAM, buf); fprintf(stdout, "NA Invalid request\n"); continue; } if ( !strncmp(buf, "YR", 2) ){ if (gss_context != GSS_C_NO_CONTEXT ) gss_delete_sec_context(&minor_status, &gss_context, NULL); gss_context = GSS_C_NO_CONTEXT; } if (strlen(buf) <= 3) { if (debug) fprintf(stderr, "%s| %s: Invalid negotiate request [%s]\n", LogTime(), PROGRAM, buf); fprintf(stdout, "NA Invalid negotiate request\n"); continue; } input_token.length = base64_decode_len(buf+3); input_token.value = malloc(input_token.length); base64_decode(input_token.value,buf+3,input_token.length); #ifndef HAVE_SPNEGO if (( rc=parseNegTokenInit (input_token.value, input_token.length, &kerberosToken, &kerberosTokenLength))!=0 ){ if (debug) fprintf(stderr, "%s| %s: parseNegTokenInit failed with rc=%d\n", LogTime(), PROGRAM, rc); /* if between 100 and 200 it might be a GSSAPI token and not a SPNEGO token */ if ( rc < 100 || rc > 199 ) { if (debug) fprintf(stderr, "%s| %s: Invalid GSS-SPNEGO query [%s]\n", LogTime(), PROGRAM, buf); fprintf(stdout, "NA Invalid GSS-SPNEGO query\n"); goto cleanup; } if ((input_token.length >= sizeof ntlmProtocol + 1) && (!memcmp (input_token.value, ntlmProtocol, sizeof ntlmProtocol))) { if (debug) fprintf(stderr, "%s| %s: received type %d NTLM token\n", LogTime(), PROGRAM, (int) *((unsigned char *)input_token.value + sizeof ntlmProtocol)); fprintf(stdout, "NA received type %d NTLM token\n",(int) *((unsigned char *)input_token.value + sizeof ntlmProtocol)); goto cleanup; } spnego_flag=0; } else { gss_release_buffer(&minor_status, &input_token); input_token.length=kerberosTokenLength; input_token.value=(void *)kerberosToken; spnego_flag=1; } #else if ((input_token.length >= sizeof ntlmProtocol + 1) && (!memcmp (input_token.value, ntlmProtocol, sizeof ntlmProtocol))) { if (debug) fprintf(stderr, "%s| %s: received type %d NTLM token\n", LogTime(), PROGRAM, (int) *((unsigned char *)input_token.value + sizeof ntlmProtocol)); fprintf(stdout, "NA received type %d NTLM token\n",(int) *((unsigned char *)input_token.value + sizeof ntlmProtocol)); goto cleanup; } #endif if ( service_principal ) { if ( strcasecmp(service_principal,"GSS_C_NO_NAME") ){ major_status = gss_import_name(&minor_status, &service, (gss_OID) GSS_C_NULL_OID, &server_name); } else { server_name = GSS_C_NO_NAME; major_status = GSS_S_COMPLETE; } } else { major_status = gss_import_name(&minor_status, &service, gss_nt_service_name, &server_name); } if ( check_gss_err(major_status,minor_status,"gss_import_name()",debug,loging) ) goto cleanup; major_status = gss_acquire_cred(&minor_status, server_name, GSS_C_INDEFINITE, GSS_C_NO_OID_SET, GSS_C_ACCEPT, &server_creds, NULL, NULL); if (check_gss_err(major_status,minor_status,"gss_acquire_cred()",debug,loging) ) goto cleanup; major_status = gss_accept_sec_context(&minor_status, &gss_context, server_creds, &input_token, GSS_C_NO_CHANNEL_BINDINGS, &client_name, NULL, &output_token, &ret_flags, NULL, &delegated_cred); if (output_token.length) { #ifndef HAVE_SPNEGO if (spnego_flag) { if ((rc=makeNegTokenTarg (output_token.value, output_token.length, &spnegoToken, &spnegoTokenLength))!=0 ) { if (debug) fprintf(stderr, "%s| %s: makeNegTokenTarg failed with rc=%d\n", LogTime(), PROGRAM, rc); fprintf(stdout, "NA makeNegTokenTarg failed with rc=%d\n",rc); goto cleanup; } } else { spnegoToken = output_token.value; spnegoTokenLength = output_token.length; } #else spnegoToken = output_token.value; spnegoTokenLength = output_token.length; #endif token = malloc(base64_encode_len(spnegoTokenLength)); if (token == NULL) { if (debug) fprintf(stderr, "%s| %s: Not enough memory\n", LogTime(), PROGRAM); fprintf(stdout, "NA Not enough memory\n"); goto cleanup; } base64_encode(token,(const char *)spnegoToken,base64_encode_len(spnegoTokenLength),spnegoTokenLength); if (check_gss_err(major_status,minor_status,"gss_accept_sec_context()",debug,loging) ) goto cleanup; if (major_status & GSS_S_CONTINUE_NEEDED) { if (debug) fprintf(stderr, "%s| %s: continuation needed\n", LogTime(), PROGRAM); fprintf(stdout, "TT %s\n",token); goto cleanup; } gss_release_buffer(&minor_status, &output_token); major_status = gss_display_name(&minor_status, client_name, &output_token, NULL); if (check_gss_err(major_status,minor_status,"gss_display_name()",debug,loging) ) goto cleanup; user=malloc(output_token.length+1); if (user == NULL) { if (debug) fprintf(stderr, "%s| %s: Not enough memory\n", LogTime(), PROGRAM); fprintf(stdout, "BH Not enough memory\n"); goto cleanup; } memcpy(user,output_token.value,output_token.length); user[output_token.length]='\0'; fprintf(stdout, "AF %s %s\n",token,user); if (debug) fprintf(stderr, "%s| %s: AF %s %s\n", LogTime(), PROGRAM, token,user); if (loging) fprintf(stderr, "%s| %s: User %s authenticated\n", LogTime(), PROGRAM, user); goto cleanup; } else { if (check_gss_err(major_status,minor_status,"gss_accept_sec_context()",debug,loging) ) goto cleanup; if (major_status & GSS_S_CONTINUE_NEEDED) { if (debug) fprintf(stderr, "%s| %s: continuation needed\n", LogTime(), PROGRAM); fprintf(stdout, "NA No token to return to continue\n"); goto cleanup; } gss_release_buffer(&minor_status, &output_token); major_status = gss_display_name(&minor_status, client_name, &output_token, NULL); if (check_gss_err(major_status,minor_status,"gss_display_name()",debug,loging) ) goto cleanup; /* * Return dummy token AA. May need an extra return tag then AF */ user=malloc(output_token.length+1); if (user == NULL) { if (debug) fprintf(stderr, "%s| %s: Not enough memory\n", LogTime(), PROGRAM); fprintf(stdout, "BH Not enough memory\n"); goto cleanup; } memcpy(user,output_token.value,output_token.length); user[output_token.length]='\0'; fprintf(stdout, "AF %s %s\n","AA==",user); if (debug) fprintf(stderr, "%s| %s: AF %s %s\n", LogTime(), PROGRAM, "AA==", user); if (loging) fprintf(stderr, "%s| %s: User %s authenticated\n", LogTime(), PROGRAM, user); cleanup: gss_release_buffer(&minor_status, &input_token); gss_release_buffer(&minor_status, &output_token); gss_release_cred(&minor_status, &server_creds); gss_release_cred(&minor_status, &delegated_cred); gss_release_name(&minor_status, &server_name); gss_release_name(&minor_status, &client_name); if (kerberosToken) { /* Allocated by parseNegTokenInit, but no matching free function exists.. */ if (!spnego_flag) free((char *)kerberosToken); kerberosToken=NULL; } if (spnego_flag) { /* Allocated by makeNegTokenTarg, but no matching free function exists.. */ if (spnegoToken) free((char *)spnegoToken); spnegoToken=NULL; } if (token) { free(token); token=NULL; } if( user) { free(user); user=NULL; } continue; } } }
static int tunnel(struct gt_service *svc, int fd, struct sockaddr *cliaddr) { AUTOCLEAN(char *tmbuf, autofreestr) = NULL; AUTOCLEAN(struct addrinfo *addr, autofreeaddrinfo) = NULL; AUTOCLEAN(int sd, autofreesocket) = -1; AUTOCLEAN(int efd, autofreesocket) = -1; AUTOCLEAN(gss_name_t name, autofreegssname) = GSS_C_NO_NAME; AUTOCLEAN(gss_name_t srcname, autofreegssname) = GSS_C_NO_NAME; AUTOCLEAN(gss_cred_id_t cred, autofreegsscred) = GSS_C_NO_CREDENTIAL; AUTOCLEAN(gss_ctx_id_t ctx, autofreegssctx) = GSS_C_NO_CONTEXT; AUTOCLEAN(gss_buffer_desc output, autofreegssbuf) = GSS_C_EMPTY_BUFFER; gss_buffer_desc input = GSS_C_EMPTY_BUFFER; gss_buffer_desc namebuf; OM_uint32 maj, min; OM_uint32 ignore; struct epoll_event events[MAX_EVENTS]; size_t tmlen; int pfd; /* plain text fd */ int cfd; /* cipher text fd */ int ret; /* We allocate a 1 MiB buffer for messages, that's also the maximum msg * size */ tmbuf = malloc(MAX_MSG_SIZE); if (!tmbuf) return ENOMEM; if (svc->exec) { fprintf(stderr, "[%s] EXEC option not supported yet, sorry!\n", svc->name); return ENOTSUP; } ret = string_to_addrinfo(svc->connect, &addr); if (ret) return ret; errno = 0; sd = socket(addr->ai_family, addr->ai_socktype, addr->ai_protocol); if (sd == -1) return errno; ret = connect(sd, addr->ai_addr, addr->ai_addrlen); if (ret != 0) { ret = errno; fprintf(stderr, "[%s] Failed to connect to server '%s': %s\n", svc->name, svc->connect, strerror(ret)); return ret; } if (svc->target_name) { namebuf.length = strlen(svc->target_name); namebuf.value = svc->target_name; maj = gss_import_name(&min, &namebuf, GSS_C_NT_HOSTBASED_SERVICE, &name); if (maj != GSS_S_COMPLETE) { fprintf(stderr, "[%s] Failed to import name: '%s' (%d/%d)\n", svc->name, svc->target_name, (int)maj, (int)min); return EINVAL; } } if (svc->client) { pfd = fd; cfd = sd; do { maj = gss_init_sec_context(&min, cred, &ctx, name, GSS_C_NO_OID, GSS_C_MUTUAL_FLAG | GSS_C_REPLAY_FLAG | GSS_C_SEQUENCE_FLAG | GSS_C_CONF_FLAG | GSS_C_INTEG_FLAG, 0, GSS_C_NO_CHANNEL_BINDINGS, &input, NULL, &output, NULL, NULL); if (maj != GSS_S_COMPLETE && maj != GSS_S_CONTINUE_NEEDED) { gt_gss_error(svc->name, GSS_C_NO_OID, maj, min); return EBADE; } if (output.length > MAX_MSG_SIZE) return ENOSPC; if (output.length > 0) { memcpy(tmbuf, output.value, output.length); tmlen = output.length; (void)gss_release_buffer(&ignore, &output); ret = send_msg(cfd, tmbuf, tmlen, true); if (ret) return ret; } if (maj == GSS_S_CONTINUE_NEEDED) { tmlen = MAX_MSG_SIZE; ret = recv_msg(cfd, tmbuf, &tmlen, true); if (ret) return ret; input.value = tmbuf; input.length = tmlen; } } while (maj == GSS_S_CONTINUE_NEEDED); } else { pfd = sd; cfd = fd; if (name != GSS_C_NO_NAME) { maj = gss_acquire_cred(&min, name, GSS_C_INDEFINITE, GSS_C_NO_OID_SET, GSS_C_ACCEPT, &cred, NULL, NULL); if (maj != GSS_S_COMPLETE) { fprintf(stderr, "[%s] Failed to acquire creds for '%s' (%d/%d)\n", svc->name, svc->target_name?svc->target_name:"", (int)maj, (int)min); return EIO; } } do { tmlen = MAX_MSG_SIZE; ret = recv_msg(cfd, tmbuf, &tmlen, true); if (ret) return ret; input.value = tmbuf; input.length = tmlen; maj = gss_accept_sec_context(&min, &ctx, cred, &input, GSS_C_NO_CHANNEL_BINDINGS, &srcname, NULL, &output, NULL, NULL, NULL); if (maj != GSS_S_COMPLETE && maj != GSS_S_CONTINUE_NEEDED) { gt_gss_error(svc->name, GSS_C_NO_OID, maj, min); return EBADE; } if (output.length > MAX_MSG_SIZE) return ENOSPC; if (output.length > 0) { memcpy(tmbuf, output.value, output.length); tmlen = output.length; (void)gss_release_buffer(&ignore, &output); ret = send_msg(cfd, tmbuf, tmlen, true); if (ret) return ret; } } while (maj == GSS_S_CONTINUE_NEEDED); } /* negotiation completed, now handle traffic */ ret = init_epoll(cfd, pfd, &efd); if (ret) return ret; while (efd != -1) { struct epoll_event *ev; int n; n = epoll_wait(efd, events, MAX_EVENTS, -1); if (n == -1) { ret = errno; if (ret == EINTR) continue; return ret; } for (int i = 0; i < n; i++) { ev = &events[i]; if (ev->events & (EPOLLERR|EPOLLHUP)) { /* one of the peers gave up */ return ENOLINK; } /* RECEIVE */ tmlen = MAX_MSG_SIZE; ret = recv_msg(ev->data.fd, tmbuf, &tmlen, (ev->data.fd == cfd)); if (ret) return ret; if (ev->data.fd == cfd) { /* sender encrypts */ input.value = tmbuf; input.length = tmlen; maj = gss_unwrap(&min, ctx, &input, &output, NULL, NULL); if (maj != GSS_S_COMPLETE) { gt_gss_error(svc->name, GSS_C_NO_OID, maj, min); return EIO; } if (output.length > MAX_MSG_SIZE) return ENOSPC; memcpy(tmbuf, output.value, output.length); tmlen = output.length; (void)gss_release_buffer(&ignore, &output); } /* RESEND */ if (ev->data.fd == pfd) { /* receiver encrypts */ input.value = tmbuf; input.length = tmlen; maj = gss_wrap(&min, ctx, 1, 0, &input, NULL, &output); if (maj != GSS_S_COMPLETE) { gt_gss_error(svc->name, GSS_C_NO_OID, maj, min); return EIO; } if (output.length > MAX_MSG_SIZE) return ENOSPC; memcpy(tmbuf, output.value, output.length); tmlen = output.length; (void)gss_release_buffer(&ignore, &output); } /* send to the other fd, add header only if we encrypted */ ret = send_msg((ev->data.fd == pfd)?cfd:pfd, tmbuf, tmlen, (ev->data.fd == pfd)); if (ret) return ret; } } return 0; }
static int HandleOP(AcceptContext) { OM_uint32 maj_stat, min_stat, ret_flags; int32_t hContext, deleg_hcred, flags; krb5_data in_token; int32_t new_context_id = 0, gsm_error = 0; krb5_data out_token = { 0 , NULL }; gss_ctx_id_t ctx; gss_cred_id_t deleg_cred = GSS_C_NO_CREDENTIAL; gss_buffer_desc input_token, output_token; ret32(c, hContext); ret32(c, flags); retdata(c, in_token); ctx = find_handle(c->handles, hContext, handle_context); if (ctx == NULL) hContext = 0; if (in_token.length) { input_token.length = in_token.length; input_token.value = in_token.data; } else { input_token.length = 0; input_token.value = NULL; } maj_stat = gss_accept_sec_context(&min_stat, &ctx, GSS_C_NO_CREDENTIAL, &input_token, GSS_C_NO_CHANNEL_BINDINGS, NULL, NULL, &output_token, &ret_flags, NULL, &deleg_cred); if (GSS_ERROR(maj_stat)) { if (hContext != 0) del_handle(&c->handles, hContext); logmessage(c, __FILE__, __LINE__, 0, "gss_accept_sec_context returns code: %d/%d", maj_stat, min_stat); new_context_id = 0; } else { if (hContext == 0) new_context_id = add_handle(c, handle_context, ctx); else new_context_id = hContext; } if (output_token.length) { out_token.data = output_token.value; out_token.length = output_token.length; } if ((ret_flags & GSS_C_DCE_STYLE) != 0) logmessage(c, __FILE__, __LINE__, 0, "accept_sec_context dce-style"); if ((ret_flags & GSS_C_DELEG_FLAG) != 0) { deleg_hcred = add_handle(c, handle_cred, deleg_cred); logmessage(c, __FILE__, __LINE__, 0, "accept_context delegated handle: %d", deleg_hcred); } else { gss_release_cred(&min_stat, &deleg_cred); deleg_hcred = 0; } gsm_error = convert_gss_to_gsm(maj_stat); put32(c, new_context_id); put32(c, gsm_error); putdata(c, out_token); put32(c, deleg_hcred); if (output_token.length) gss_release_buffer(&min_stat, &output_token); krb5_data_free(&in_token); return 0; }
static int test_libntlm_v1(int flags) { const char *user = "******", *domain = "mydomain", *password = "******"; OM_uint32 maj_stat, min_stat; gss_ctx_id_t ctx = GSS_C_NO_CONTEXT; gss_buffer_desc input, output; struct ntlm_type1 type1; struct ntlm_type2 type2; struct ntlm_type3 type3; struct ntlm_buf data; krb5_error_code ret; gss_name_t src_name = GSS_C_NO_NAME; memset(&type1, 0, sizeof(type1)); memset(&type2, 0, sizeof(type2)); memset(&type3, 0, sizeof(type3)); type1.flags = NTLM_NEG_UNICODE|NTLM_NEG_TARGET|NTLM_NEG_NTLM|flags; type1.domain = strdup(domain); type1.hostname = NULL; type1.os[0] = 0; type1.os[1] = 0; ret = heim_ntlm_encode_type1(&type1, &data); if (ret) errx(1, "heim_ntlm_encode_type1"); input.value = data.data; input.length = data.length; output.length = 0; output.value = NULL; maj_stat = gss_accept_sec_context(&min_stat, &ctx, GSS_C_NO_CREDENTIAL, &input, GSS_C_NO_CHANNEL_BINDINGS, NULL, NULL, &output, NULL, NULL, NULL); free(data.data); if (GSS_ERROR(maj_stat)) errx(1, "accept_sec_context v1: %s", gssapi_err(maj_stat, min_stat, GSS_C_NO_OID)); if (output.length == 0) errx(1, "output.length == 0"); data.data = output.value; data.length = output.length; ret = heim_ntlm_decode_type2(&data, &type2); if (ret) errx(1, "heim_ntlm_decode_type2"); gss_release_buffer(&min_stat, &output); type3.flags = type2.flags; type3.username = rk_UNCONST(user); type3.targetname = type2.targetname; type3.ws = rk_UNCONST("workstation"); { struct ntlm_buf key; heim_ntlm_nt_key(password, &key); heim_ntlm_calculate_ntlm1(key.data, key.length, type2.challenge, &type3.ntlm); if (flags & NTLM_NEG_KEYEX) { struct ntlm_buf sessionkey; heim_ntlm_build_ntlm1_master(key.data, key.length, &sessionkey, &type3.sessionkey); free(sessionkey.data); } free(key.data); } ret = heim_ntlm_encode_type3(&type3, &data); if (ret) errx(1, "heim_ntlm_encode_type3"); input.length = data.length; input.value = data.data; maj_stat = gss_accept_sec_context(&min_stat, &ctx, GSS_C_NO_CREDENTIAL, &input, GSS_C_NO_CHANNEL_BINDINGS, &src_name, NULL, &output, NULL, NULL, NULL); free(input.value); if (maj_stat != GSS_S_COMPLETE) errx(1, "accept_sec_context v1 2 %s", gssapi_err(maj_stat, min_stat, GSS_C_NO_OID)); gss_release_buffer(&min_stat, &output); gss_delete_sec_context(&min_stat, &ctx, NULL); if (src_name == GSS_C_NO_NAME) errx(1, "no source name!"); gss_display_name(&min_stat, src_name, &output, NULL); printf("src_name: %.*s\n", (int)output.length, (char*)output.value); gss_release_name(&min_stat, &src_name); gss_release_buffer(&min_stat, &output); return 0; }
int main(int argc, char *argv[]) { OM_uint32 minor, major; gss_cred_id_t initiator_cred = GSS_C_NO_CREDENTIAL; gss_name_t target_name, initiator_name = GSS_C_NO_NAME; gss_name_t real_initiator_name; gss_buffer_desc token, tmp, namebuf; gss_ctx_id_t initiator_context = GSS_C_NO_CONTEXT; gss_ctx_id_t acceptor_context = GSS_C_NO_CONTEXT; if (argc < 2 || argc > 3) { fprintf(stderr, "Usage: %s targetname [initiatorname|-]\n", argv[0]); return 1; } target_name = import_name(argv[1]); if (argc >= 3) { /* Get initiator cred. */ if (strcmp(argv[2], "-") != 0) initiator_name = import_name(argv[2]); major = gss_acquire_cred(&minor, initiator_name, GSS_C_INDEFINITE, GSS_C_NO_OID_SET, GSS_C_INITIATE, &initiator_cred, NULL, NULL); check_gsserr("gss_acquire_cred", major, minor); } /* Create krb5 initiator context and get the first token. */ token.value = NULL; token.length = 0; major = gss_init_sec_context(&minor, initiator_cred, &initiator_context, target_name, (gss_OID)gss_mech_krb5, GSS_C_REPLAY_FLAG | GSS_C_SEQUENCE_FLAG, GSS_C_INDEFINITE, GSS_C_NO_CHANNEL_BINDINGS, GSS_C_NO_BUFFER, NULL, &token, NULL, NULL); check_gsserr("gss_init_sec_context", major, minor); /* Pass the token to gss_accept_sec_context. */ tmp.value = NULL; tmp.length = 0; major = gss_accept_sec_context(&minor, &acceptor_context, GSS_C_NO_CREDENTIAL, &token, GSS_C_NO_CHANNEL_BINDINGS, &real_initiator_name, NULL, &tmp, NULL, NULL, NULL); check_gsserr("gss_accept_sec_context", major, minor); namebuf.value = NULL; namebuf.length = 0; major = gss_display_name(&minor, real_initiator_name, &namebuf, NULL); check_gsserr("gss_display_name(initiator)", major, minor); printf("%.*s\n", (int)namebuf.length, (char *)namebuf.value); (void)gss_release_name(&minor, &target_name); (void)gss_release_name(&minor, &initiator_name); (void)gss_release_name(&minor, &real_initiator_name); (void)gss_release_cred(&minor, &initiator_cred); (void)gss_delete_sec_context(&minor, &initiator_context, NULL); (void)gss_delete_sec_context(&minor, &acceptor_context, NULL); (void)gss_release_buffer(&minor, &token); (void)gss_release_buffer(&minor, &tmp); (void)gss_release_buffer(&minor, &namebuf); return 0; }
/* * 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); }
int gsslib_put_credentials(gss_cred_id_t server_creds, gss_buffer_desc *cred, char *username) { gss_ctx_id_t context = GSS_C_NO_CONTEXT; gss_buffer_desc client_name; OM_uint32 maj_stat, min_stat; GSSAPI_INT ret_flags; gss_buffer_desc send_tok; gss_name_t client = NULL; gss_OID doid; int cc=0; gss_cred_id_t delegated_cred = GSS_C_NO_CREDENTIAL; gsslib_reset_error(); send_tok.length = 0; client_name.length = 0; if (cred->length <= 0) { gsslib_print_error(MSG_GSS_PRINTERROR_CREDENTIALBUFFERLENGTHISZERO ); cc = -1; goto error; } /* * establish and forward client credentials */ maj_stat = gss_accept_sec_context(&min_stat, &context, server_creds, cred, GSS_C_NO_CHANNEL_BINDINGS, &client, &doid, &send_tok, &ret_flags, NULL, /* ignore time_rec */ &delegated_cred); /* ignore del_cred_handle */ if (maj_stat!=GSS_S_COMPLETE && maj_stat!=GSS_S_CONTINUE_NEEDED) { gsslib_display_status(MSG_GSS_DISPLAYSTATUS_ACCEPTINGCONTEXT, maj_stat, min_stat); cc = -1; goto error; } if (send_tok.length != 0) { fprintf(stderr, "%s\n", MSG_GSS_ACCEPTSECCONTEXTREQUIRESTOKENTOBESENTBACK ); /* cc = -1; goto error; */ } maj_stat = gss_display_name(&min_stat, client, &client_name, &doid); if (maj_stat != GSS_S_COMPLETE) { gsslib_display_status(MSG_GSS_DISPLAYSTATUS_DISPLAYINGNAME, maj_stat, min_stat); cc = -1; goto error; } #ifdef KRBGSS #ifdef KRB5_EXPORTVAR /* this is required for later Kerberos versions */ /* check for delegated credential */ if (delegated_cred == GSS_C_NO_CREDENTIAL) { fprintf(stderr, "WARNING: Credentials were not forwarded\n"); #ifdef REQUIRE_FORWARDED_CREDENTIALS cc = 3; goto error; #endif } if (username && (ret_flags & GSS_C_DELEG_FLAG)) { char *principal = malloc(client_name.length + 1); strncpy(principal, client_name.value, client_name.length); principal[client_name.length] = 0; put_creds_in_ccache(principal, delegated_cred); free(principal); } #endif #endif /* display the flags */ if (verbose) gsslib_display_ctx_flags(ret_flags); if (verbose) printf("client: \"%.*s\"\n", (int) client_name.length, (char *) client_name.value); if (username) { gss_buffer_desc tok; gss_name_t user_name; int str_equal; tok.value = username; tok.length = strlen(tok.value)+1; maj_stat = gss_import_name(&min_stat, &tok, GSS_C_NULL_OID, &user_name); if (maj_stat != GSS_S_COMPLETE) { gsslib_display_status(MSG_GSS_DISPLAYSTATUS_PARSINGNAME, maj_stat, min_stat); cc = -1; goto error; } maj_stat = gss_compare_name(&min_stat, client, user_name, &str_equal); if (maj_stat != GSS_S_COMPLETE) { gsslib_display_status( MSG_GSS_DISPLAYSTATUS_DISPLAYINGNAME, maj_stat, min_stat); cc = 6; goto error; } #ifdef KRBGSS if (!str_equal) { krb5_context context; maj_stat = krb5_init_context(&context); if (maj_stat != GSS_S_COMPLETE) { gsslib_display_status(MSG_GSS_DISPLAYSTATUS_GETTINGKRB5CONTEXT, maj_stat, GSS_S_COMPLETE); cc = -1; goto error; } /* see if this user is authorized by the krb5 client */ if (krb5_kuserok(context, (krb5_principal)client, username)) str_equal = 1; } /* Users from Kerberos cross-authenticated realms will not match, so we manually compare the user names */ if (!str_equal) { char *s; if ((s=strchr((char *)client_name.value, '@'))) str_equal = !strncmp(username, (char *)client_name.value, s-(char *)client_name.value); } #endif if (!str_equal) { char buf[1024]; snprintf(buf, sizeof(buf), MSG_GSS_CLIENTNAMEXDOESNOTMATCHUNAMEY_SS, (int)client_name.length, (char *)client_name.value, username); gsslib_print_error(buf); cc = 5; goto error; } } #ifdef DCE while (delegated_cred) { sec_login_handle_t login_context; error_status_t st; dce_error_string_t err_string; int lst; sec_login_auth_src_t auth_src=NULL; boolean32 reset_passwd=0; char errbuf[1024]; unsigned32 num_groups=0; signed32 *groups=NULL; unsigned32 flags; maj_stat = gssdce_set_cred_context_ownership(&min_stat, delegated_cred, GSSDCE_C_OWNERSHIP_APPLICATION); if (maj_stat != GSS_S_COMPLETE) { gsslib_display_status(MSG_GSS_DISPLAYSTATUS_GSSDCESETCREDCONTEXTOWNERSHIP, maj_stat, min_stat); break; } #if 0 gsslib_print_error(MSG_GSS_PRINTERROR_CREDENTIALDUMP); gsslib_print_error(dump_cred(delegated_cred)); #endif maj_stat = gssdce_cred_to_login_context(&min_stat, delegated_cred, &login_context); if (maj_stat != GSS_S_COMPLETE) { gsslib_display_status(MSG_GSS_DISPLAYSTATUS_GSSDCECREDTOLOGINCONTEXT, maj_stat, min_stat); break; } #ifdef TURN_OFF_DELEGATION { sec_login_handle_t *new_login_context; new_login_context = sec_login_disable_delegation(login_context, &st); if (st != error_status_ok) { dce_error_inq_text(st, err_string, &lst); snprintf(errbuf, sizeof errbuf, MSG_GSS_PRINTERROR_COULDNOTDISABLEDELEGATIONX_S, err_string); gsslib_print_error(errbuf); } else { login_context = *new_login_context; } } #endif flags = sec_login_get_context_flags(login_context, &st); sec_login_set_context_flags(login_context, flags & ~sec_login_credentials_private, &st); if (!sec_login_certify_identity(login_context, &st)) { dce_error_inq_text(st, err_string, &lst); snprintf(errbuf, sizeof errbuf, MSG_GSS_PRINTERROR_COULDNOTCERTIFYIDENTITYX_S, err_string); gsslib_print_error(errbuf); break; } sec_login_set_context(login_context, &st); if (st != error_status_ok) { dce_error_inq_text(st, err_string, &lst); snprintf(errbuf, sizeof errbuf, MSG_GSS_PRINTERROR_COULDNOTSETUPLOGINCONTEXTX_S, err_string); gsslib_print_error(errbuf); break; } { char *cp; cp = getenv("KRB5CCNAME"); if (cp) { snprintf(errbuf, sizeof errbuf, MSG_GSS_PRINTERROR_NEWKRB5CCNAMEISX_S , cp); gsslib_print_error(errbuf); } else { gsslib_print_error(MSG_GSS_PRINTERROR_KRB5CCNAMENOTFOUND ); } } break; } #endif /* DCE */ error: if (client) { maj_stat = gss_release_name(&min_stat, &client); if (maj_stat != GSS_S_COMPLETE) { gsslib_display_status(MSG_GSS_DISPLAYSTATUS_RELEASINGNAME, maj_stat, min_stat); cc = -1; } } if (send_tok.length) (void) gss_release_buffer(&min_stat, &send_tok); if (client_name.length) (void) gss_release_buffer(&min_stat, &client_name); return cc; }
isc_result_t dst_gssapi_acceptctx(gss_cred_id_t cred, const char *gssapi_keytab, isc_region_t *intoken, isc_buffer_t **outtoken, gss_ctx_id_t *ctxout, dns_name_t *principal, isc_mem_t *mctx) { #ifdef GSSAPI isc_region_t r; isc_buffer_t namebuf; gss_buffer_desc gnamebuf = GSS_C_EMPTY_BUFFER, gintoken, gouttoken = GSS_C_EMPTY_BUFFER; OM_uint32 gret, minor; gss_ctx_id_t context = GSS_C_NO_CONTEXT; gss_name_t gname = NULL; isc_result_t result; char buf[1024]; REQUIRE(outtoken != NULL && *outtoken == NULL); log_cred(cred); REGION_TO_GBUFFER(*intoken, gintoken); if (*ctxout == NULL) context = GSS_C_NO_CONTEXT; else context = *ctxout; if (gssapi_keytab != NULL) { #ifdef ISC_PLATFORM_GSSAPI_KRB5_HEADER gret = gsskrb5_register_acceptor_identity(gssapi_keytab); if (gret != GSS_S_COMPLETE) { gss_log(3, "failed " "gsskrb5_register_acceptor_identity(%s): %s", gssapi_keytab, gss_error_tostring(gret, minor, buf, sizeof(buf))); return (DNS_R_INVALIDTKEY); } #else /* * Minimize memory leakage by only setting KRB5_KTNAME * if it needs to change. */ const char *old = getenv("KRB5_KTNAME"); if (old == NULL || strcmp(old, gssapi_keytab) != 0) { char *kt = malloc(strlen(gssapi_keytab) + 13); if (kt == NULL) return (ISC_R_NOMEMORY); sprintf(kt, "KRB5_KTNAME=%s", gssapi_keytab); if (putenv(kt) != 0) return (ISC_R_NOMEMORY); } #endif } gret = gss_accept_sec_context(&minor, &context, cred, &gintoken, GSS_C_NO_CHANNEL_BINDINGS, &gname, NULL, &gouttoken, NULL, NULL, NULL); result = ISC_R_FAILURE; switch (gret) { case GSS_S_COMPLETE: result = ISC_R_SUCCESS; break; case GSS_S_CONTINUE_NEEDED: result = DNS_R_CONTINUE; break; case GSS_S_DEFECTIVE_TOKEN: case GSS_S_DEFECTIVE_CREDENTIAL: case GSS_S_BAD_SIG: case GSS_S_DUPLICATE_TOKEN: case GSS_S_OLD_TOKEN: case GSS_S_NO_CRED: case GSS_S_CREDENTIALS_EXPIRED: case GSS_S_BAD_BINDINGS: case GSS_S_NO_CONTEXT: case GSS_S_BAD_MECH: case GSS_S_FAILURE: result = DNS_R_INVALIDTKEY; /* fall through */ default: gss_log(3, "failed gss_accept_sec_context: %s", gss_error_tostring(gret, minor, buf, sizeof(buf))); return (result); } if (gouttoken.length > 0) { RETERR(isc_buffer_allocate(mctx, outtoken, gouttoken.length)); GBUFFER_TO_REGION(gouttoken, r); RETERR(isc_buffer_copyregion(*outtoken, &r)); (void)gss_release_buffer(&minor, &gouttoken); } if (gret == GSS_S_COMPLETE) { gret = gss_display_name(&minor, gname, &gnamebuf, NULL); if (gret != GSS_S_COMPLETE) { gss_log(3, "failed gss_display_name: %s", gss_error_tostring(gret, minor, buf, sizeof(buf))); RETERR(ISC_R_FAILURE); } /* * Compensate for a bug in Solaris8's implementation * of gss_display_name(). Should be harmless in any * case, since principal names really should not * contain null characters. */ if (gnamebuf.length > 0 && ((char *)gnamebuf.value)[gnamebuf.length - 1] == '\0') gnamebuf.length--; gss_log(3, "gss-api source name (accept) is %.*s", (int)gnamebuf.length, (char *)gnamebuf.value); GBUFFER_TO_REGION(gnamebuf, r); isc_buffer_init(&namebuf, r.base, r.length); isc_buffer_add(&namebuf, r.length); RETERR(dns_name_fromtext(principal, &namebuf, dns_rootname, 0, NULL)); if (gnamebuf.length != 0) { gret = gss_release_buffer(&minor, &gnamebuf); if (gret != GSS_S_COMPLETE) gss_log(3, "failed gss_release_buffer: %s", gss_error_tostring(gret, minor, buf, sizeof(buf))); } } *ctxout = context; out: if (gname != NULL) { gret = gss_release_name(&minor, &gname); if (gret != GSS_S_COMPLETE) gss_log(3, "failed gss_release_name: %s", gss_error_tostring(gret, minor, buf, sizeof(buf))); } return (result); #else UNUSED(cred); UNUSED(gssapi_keytab); UNUSED(intoken); UNUSED(outtoken); UNUSED(ctxout); UNUSED(principal); UNUSED(mctx); return (ISC_R_NOTIMPLEMENTED); #endif }
static NTSTATUS gensec_gssapi_update(struct gensec_security *gensec_security, TALLOC_CTX *out_mem_ctx, struct tevent_context *ev, const DATA_BLOB in, DATA_BLOB *out) { struct gensec_gssapi_state *gensec_gssapi_state = talloc_get_type(gensec_security->private_data, struct gensec_gssapi_state); NTSTATUS nt_status = NT_STATUS_LOGON_FAILURE; OM_uint32 maj_stat, min_stat; OM_uint32 min_stat2; gss_buffer_desc input_token = { 0, NULL }; gss_buffer_desc output_token = { 0, NULL }; gss_OID gss_oid_p = NULL; OM_uint32 time_req = 0; OM_uint32 time_rec = 0; struct timeval tv; time_req = gensec_setting_int(gensec_security->settings, "gensec_gssapi", "requested_life_time", time_req); input_token.length = in.length; input_token.value = in.data; switch (gensec_gssapi_state->sasl_state) { case STAGE_GSS_NEG: { switch (gensec_security->gensec_role) { case GENSEC_CLIENT: { #ifdef SAMBA4_USES_HEIMDAL struct gsskrb5_send_to_kdc send_to_kdc; krb5_error_code ret; #endif nt_status = gensec_gssapi_client_creds(gensec_security, ev); if (!NT_STATUS_IS_OK(nt_status)) { return nt_status; } #ifdef SAMBA4_USES_HEIMDAL send_to_kdc.func = smb_krb5_send_and_recv_func; send_to_kdc.ptr = ev; min_stat = gsskrb5_set_send_to_kdc(&send_to_kdc); if (min_stat) { DEBUG(1,("gensec_gssapi_update: gsskrb5_set_send_to_kdc failed\n")); return NT_STATUS_INTERNAL_ERROR; } #endif maj_stat = gss_init_sec_context(&min_stat, gensec_gssapi_state->client_cred->creds, &gensec_gssapi_state->gssapi_context, gensec_gssapi_state->server_name, gensec_gssapi_state->gss_oid, gensec_gssapi_state->gss_want_flags, time_req, gensec_gssapi_state->input_chan_bindings, &input_token, &gss_oid_p, &output_token, &gensec_gssapi_state->gss_got_flags, /* ret flags */ &time_rec); if (gss_oid_p) { gensec_gssapi_state->gss_oid = gss_oid_p; } #ifdef SAMBA4_USES_HEIMDAL send_to_kdc.func = smb_krb5_send_and_recv_func; send_to_kdc.ptr = NULL; ret = gsskrb5_set_send_to_kdc(&send_to_kdc); if (ret) { DEBUG(1,("gensec_gssapi_update: gsskrb5_set_send_to_kdc failed\n")); return NT_STATUS_INTERNAL_ERROR; } #endif break; } case GENSEC_SERVER: { maj_stat = gss_accept_sec_context(&min_stat, &gensec_gssapi_state->gssapi_context, gensec_gssapi_state->server_cred->creds, &input_token, gensec_gssapi_state->input_chan_bindings, &gensec_gssapi_state->client_name, &gss_oid_p, &output_token, &gensec_gssapi_state->gss_got_flags, &time_rec, &gensec_gssapi_state->delegated_cred_handle); if (gss_oid_p) { gensec_gssapi_state->gss_oid = gss_oid_p; } break; } default: return NT_STATUS_INVALID_PARAMETER; } gensec_gssapi_state->gss_exchange_count++; if (maj_stat == GSS_S_COMPLETE) { *out = data_blob_talloc(out_mem_ctx, output_token.value, output_token.length); gss_release_buffer(&min_stat2, &output_token); if (gensec_gssapi_state->gss_got_flags & GSS_C_DELEG_FLAG && gensec_gssapi_state->delegated_cred_handle != GSS_C_NO_CREDENTIAL) { DEBUG(5, ("gensec_gssapi: credentials were delegated\n")); } else { DEBUG(5, ("gensec_gssapi: NO credentials were delegated\n")); } tv = timeval_current_ofs(time_rec, 0); gensec_gssapi_state->expire_time = timeval_to_nttime(&tv); /* We may have been invoked as SASL, so there * is more work to do */ if (gensec_gssapi_state->sasl) { gensec_gssapi_state->sasl_state = STAGE_SASL_SSF_NEG; return NT_STATUS_MORE_PROCESSING_REQUIRED; } else { gensec_gssapi_state->sasl_state = STAGE_DONE; if (gensec_have_feature(gensec_security, GENSEC_FEATURE_SEAL)) { DEBUG(5, ("GSSAPI Connection will be cryptographically sealed\n")); } else if (gensec_have_feature(gensec_security, GENSEC_FEATURE_SIGN)) { DEBUG(5, ("GSSAPI Connection will be cryptographically signed\n")); } else { DEBUG(5, ("GSSAPI Connection will have no cryptographic protection\n")); } return NT_STATUS_OK; } } else if (maj_stat == GSS_S_CONTINUE_NEEDED) { *out = data_blob_talloc(out_mem_ctx, output_token.value, output_token.length); gss_release_buffer(&min_stat2, &output_token); return NT_STATUS_MORE_PROCESSING_REQUIRED; } else if (maj_stat == GSS_S_CONTEXT_EXPIRED) { gss_cred_id_t creds = NULL; gss_name_t name; gss_buffer_desc buffer; OM_uint32 lifetime = 0; gss_cred_usage_t usage; const char *role = NULL; DEBUG(0, ("GSS %s Update(krb5)(%d) Update failed, credentials expired during GSSAPI handshake!\n", role, gensec_gssapi_state->gss_exchange_count)); switch (gensec_security->gensec_role) { case GENSEC_CLIENT: creds = gensec_gssapi_state->client_cred->creds; role = "client"; break; case GENSEC_SERVER: creds = gensec_gssapi_state->server_cred->creds; role = "server"; break; } maj_stat = gss_inquire_cred(&min_stat, creds, &name, &lifetime, &usage, NULL); if (maj_stat == GSS_S_COMPLETE) { const char *usage_string = NULL; switch (usage) { case GSS_C_BOTH: usage_string = "GSS_C_BOTH"; break; case GSS_C_ACCEPT: usage_string = "GSS_C_ACCEPT"; break; case GSS_C_INITIATE: usage_string = "GSS_C_INITIATE"; break; } maj_stat = gss_display_name(&min_stat, name, &buffer, NULL); if (maj_stat) { buffer.value = NULL; buffer.length = 0; } if (lifetime > 0) { DEBUG(0, ("GSSAPI gss_inquire_cred indicates expiry of %*.*s in %u sec for %s\n", (int)buffer.length, (int)buffer.length, (char *)buffer.value, lifetime, usage_string)); } else { DEBUG(0, ("GSSAPI gss_inquire_cred indicates %*.*s has already expired for %s\n", (int)buffer.length, (int)buffer.length, (char *)buffer.value, usage_string)); } gss_release_buffer(&min_stat, &buffer); gss_release_name(&min_stat, &name); } else if (maj_stat != GSS_S_COMPLETE) { DEBUG(0, ("inquiry of credential lifefime via GSSAPI gss_inquire_cred failed: %s\n", gssapi_error_string(out_mem_ctx, maj_stat, min_stat, gensec_gssapi_state->gss_oid))); } return NT_STATUS_INVALID_PARAMETER; } else if (smb_gss_oid_equal(gensec_gssapi_state->gss_oid, gss_mech_krb5)) { switch (min_stat) { case KRB5KRB_AP_ERR_TKT_NYV: DEBUG(1, ("Error with ticket to contact %s: possible clock skew between us and the KDC or target server: %s\n", gensec_gssapi_state->target_principal, gssapi_error_string(out_mem_ctx, maj_stat, min_stat, gensec_gssapi_state->gss_oid))); return NT_STATUS_TIME_DIFFERENCE_AT_DC; /* Make SPNEGO ignore us, we can't go any further here */ case KRB5KRB_AP_ERR_TKT_EXPIRED: DEBUG(1, ("Error with ticket to contact %s: ticket is expired, possible clock skew between us and the KDC or target server: %s\n", gensec_gssapi_state->target_principal, gssapi_error_string(out_mem_ctx, maj_stat, min_stat, gensec_gssapi_state->gss_oid))); return NT_STATUS_INVALID_PARAMETER; /* Make SPNEGO ignore us, we can't go any further here */ case KRB5_KDC_UNREACH: DEBUG(3, ("Cannot reach a KDC we require in order to obtain a ticket to %s: %s\n", gensec_gssapi_state->target_principal, gssapi_error_string(out_mem_ctx, maj_stat, min_stat, gensec_gssapi_state->gss_oid))); return NT_STATUS_NO_LOGON_SERVERS; /* Make SPNEGO ignore us, we can't go any further here */ case KRB5KDC_ERR_S_PRINCIPAL_UNKNOWN: DEBUG(3, ("Server %s is not registered with our KDC: %s\n", gensec_gssapi_state->target_principal, gssapi_error_string(out_mem_ctx, maj_stat, min_stat, gensec_gssapi_state->gss_oid))); return NT_STATUS_INVALID_PARAMETER; /* Make SPNEGO ignore us, we can't go any further here */ case KRB5KRB_AP_ERR_MSG_TYPE: /* garbage input, possibly from the auto-mech detection */ return NT_STATUS_INVALID_PARAMETER; default: DEBUG(1, ("GSS %s Update(krb5)(%d) Update failed: %s\n", gensec_security->gensec_role == GENSEC_CLIENT ? "client" : "server", gensec_gssapi_state->gss_exchange_count, gssapi_error_string(out_mem_ctx, maj_stat, min_stat, gensec_gssapi_state->gss_oid))); return NT_STATUS_LOGON_FAILURE; } } else { DEBUG(1, ("GSS %s Update(%d) failed: %s\n", gensec_security->gensec_role == GENSEC_CLIENT ? "client" : "server", gensec_gssapi_state->gss_exchange_count, gssapi_error_string(out_mem_ctx, maj_stat, min_stat, gensec_gssapi_state->gss_oid))); return NT_STATUS_LOGON_FAILURE; } break; } /* These last two stages are only done if we were invoked as SASL */ case STAGE_SASL_SSF_NEG: { switch (gensec_security->gensec_role) { case GENSEC_CLIENT: { uint8_t maxlength_proposed[4]; uint8_t maxlength_accepted[4]; uint8_t security_supported; int conf_state; gss_qop_t qop_state; input_token.length = in.length; input_token.value = in.data; /* As a client, we have just send a * zero-length blob to the server (after the * normal GSSAPI exchange), and it has replied * with it's SASL negotiation */ maj_stat = gss_unwrap(&min_stat, gensec_gssapi_state->gssapi_context, &input_token, &output_token, &conf_state, &qop_state); if (GSS_ERROR(maj_stat)) { DEBUG(1, ("gensec_gssapi_update: GSS UnWrap of SASL protection negotiation failed: %s\n", gssapi_error_string(out_mem_ctx, maj_stat, min_stat, gensec_gssapi_state->gss_oid))); return NT_STATUS_ACCESS_DENIED; } if (output_token.length < 4) { return NT_STATUS_INVALID_PARAMETER; } memcpy(maxlength_proposed, output_token.value, 4); gss_release_buffer(&min_stat, &output_token); /* first byte is the proposed security */ security_supported = maxlength_proposed[0]; maxlength_proposed[0] = '\0'; /* Rest is the proposed max wrap length */ gensec_gssapi_state->max_wrap_buf_size = MIN(RIVAL(maxlength_proposed, 0), gensec_gssapi_state->max_wrap_buf_size); gensec_gssapi_state->sasl_protection = 0; if (security_supported & NEG_SEAL) { if (gensec_have_feature(gensec_security, GENSEC_FEATURE_SEAL)) { gensec_gssapi_state->sasl_protection |= NEG_SEAL; } } if (security_supported & NEG_SIGN) { if (gensec_have_feature(gensec_security, GENSEC_FEATURE_SIGN)) { gensec_gssapi_state->sasl_protection |= NEG_SIGN; } } if (security_supported & NEG_NONE) { gensec_gssapi_state->sasl_protection |= NEG_NONE; } if (gensec_gssapi_state->sasl_protection == 0) { DEBUG(1, ("Remote server does not support unprotected connections\n")); return NT_STATUS_ACCESS_DENIED; } /* Send back the negotiated max length */ RSIVAL(maxlength_accepted, 0, gensec_gssapi_state->max_wrap_buf_size); maxlength_accepted[0] = gensec_gssapi_state->sasl_protection; input_token.value = maxlength_accepted; input_token.length = sizeof(maxlength_accepted); maj_stat = gss_wrap(&min_stat, gensec_gssapi_state->gssapi_context, false, GSS_C_QOP_DEFAULT, &input_token, &conf_state, &output_token); if (GSS_ERROR(maj_stat)) { DEBUG(1, ("GSS Update(SSF_NEG): GSS Wrap failed: %s\n", gssapi_error_string(out_mem_ctx, maj_stat, min_stat, gensec_gssapi_state->gss_oid))); return NT_STATUS_ACCESS_DENIED; } *out = data_blob_talloc(out_mem_ctx, output_token.value, output_token.length); gss_release_buffer(&min_stat, &output_token); /* quirk: This changes the value that gensec_have_feature returns, to be that after SASL negotiation */ gensec_gssapi_state->sasl_state = STAGE_DONE; if (gensec_have_feature(gensec_security, GENSEC_FEATURE_SEAL)) { DEBUG(3, ("SASL/GSSAPI Connection to server will be cryptographically sealed\n")); } else if (gensec_have_feature(gensec_security, GENSEC_FEATURE_SIGN)) { DEBUG(3, ("SASL/GSSAPI Connection to server will be cryptographically signed\n")); } else { DEBUG(3, ("SASL/GSSAPI Connection to server will have no cryptographically protection\n")); } return NT_STATUS_OK; } case GENSEC_SERVER: { uint8_t maxlength_proposed[4]; uint8_t security_supported = 0x0; int conf_state; /* As a server, we have just been sent a zero-length blob (note this, but it isn't fatal) */ if (in.length != 0) { DEBUG(1, ("SASL/GSSAPI: client sent non-zero length starting SASL negotiation!\n")); } /* Give the client some idea what we will support */ RSIVAL(maxlength_proposed, 0, gensec_gssapi_state->max_wrap_buf_size); /* first byte is the proposed security */ maxlength_proposed[0] = '\0'; gensec_gssapi_state->sasl_protection = 0; if (gensec_have_feature(gensec_security, GENSEC_FEATURE_SEAL)) { security_supported |= NEG_SEAL; } if (gensec_have_feature(gensec_security, GENSEC_FEATURE_SIGN)) { security_supported |= NEG_SIGN; } if (security_supported == 0) { /* If we don't support anything, this must be 0 */ RSIVAL(maxlength_proposed, 0, 0x0); } /* TODO: We may not wish to support this */ security_supported |= NEG_NONE; maxlength_proposed[0] = security_supported; input_token.value = maxlength_proposed; input_token.length = sizeof(maxlength_proposed); maj_stat = gss_wrap(&min_stat, gensec_gssapi_state->gssapi_context, false, GSS_C_QOP_DEFAULT, &input_token, &conf_state, &output_token); if (GSS_ERROR(maj_stat)) { DEBUG(1, ("GSS Update(SSF_NEG): GSS Wrap failed: %s\n", gssapi_error_string(out_mem_ctx, maj_stat, min_stat, gensec_gssapi_state->gss_oid))); return NT_STATUS_ACCESS_DENIED; } *out = data_blob_talloc(out_mem_ctx, output_token.value, output_token.length); gss_release_buffer(&min_stat, &output_token); gensec_gssapi_state->sasl_state = STAGE_SASL_SSF_ACCEPT; return NT_STATUS_MORE_PROCESSING_REQUIRED; } default: return NT_STATUS_INVALID_PARAMETER; } } /* This is s server-only stage */ case STAGE_SASL_SSF_ACCEPT: { uint8_t maxlength_accepted[4]; uint8_t security_accepted; int conf_state; gss_qop_t qop_state; input_token.length = in.length; input_token.value = in.data; maj_stat = gss_unwrap(&min_stat, gensec_gssapi_state->gssapi_context, &input_token, &output_token, &conf_state, &qop_state); if (GSS_ERROR(maj_stat)) { DEBUG(1, ("gensec_gssapi_update: GSS UnWrap of SASL protection negotiation failed: %s\n", gssapi_error_string(out_mem_ctx, maj_stat, min_stat, gensec_gssapi_state->gss_oid))); return NT_STATUS_ACCESS_DENIED; } if (output_token.length < 4) { return NT_STATUS_INVALID_PARAMETER; } memcpy(maxlength_accepted, output_token.value, 4); gss_release_buffer(&min_stat, &output_token); /* first byte is the proposed security */ security_accepted = maxlength_accepted[0]; maxlength_accepted[0] = '\0'; /* Rest is the proposed max wrap length */ gensec_gssapi_state->max_wrap_buf_size = MIN(RIVAL(maxlength_accepted, 0), gensec_gssapi_state->max_wrap_buf_size); gensec_gssapi_state->sasl_protection = 0; if (security_accepted & NEG_SEAL) { if (!gensec_have_feature(gensec_security, GENSEC_FEATURE_SEAL)) { DEBUG(1, ("Remote client wanted seal, but gensec refused\n")); return NT_STATUS_ACCESS_DENIED; } gensec_gssapi_state->sasl_protection |= NEG_SEAL; } if (security_accepted & NEG_SIGN) { if (!gensec_have_feature(gensec_security, GENSEC_FEATURE_SIGN)) { DEBUG(1, ("Remote client wanted sign, but gensec refused\n")); return NT_STATUS_ACCESS_DENIED; } gensec_gssapi_state->sasl_protection |= NEG_SIGN; } if (security_accepted & NEG_NONE) { gensec_gssapi_state->sasl_protection |= NEG_NONE; } /* quirk: This changes the value that gensec_have_feature returns, to be that after SASL negotiation */ gensec_gssapi_state->sasl_state = STAGE_DONE; if (gensec_have_feature(gensec_security, GENSEC_FEATURE_SEAL)) { DEBUG(5, ("SASL/GSSAPI Connection from client will be cryptographically sealed\n")); } else if (gensec_have_feature(gensec_security, GENSEC_FEATURE_SIGN)) { DEBUG(5, ("SASL/GSSAPI Connection from client will be cryptographically signed\n")); } else { DEBUG(5, ("SASL/GSSAPI Connection from client will have no cryptographic protection\n")); } *out = data_blob(NULL, 0); return NT_STATUS_OK; } default: return NT_STATUS_INVALID_PARAMETER; } }