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; }
static OM_uint32 initiator_approved(gss_name_t target_name, gss_OID mech) { OM_uint32 min_stat, maj_stat; gss_ctx_id_t ctx = GSS_C_NO_CONTEXT; gss_buffer_desc out; maj_stat = gss_init_sec_context(&min_stat, GSS_C_NO_CREDENTIAL, &ctx, target_name, mech, 0, GSS_C_INDEFINITE, GSS_C_NO_CHANNEL_BINDINGS, GSS_C_NO_BUFFER, NULL, &out, NULL, NULL); if (GSS_ERROR(maj_stat)) { gss_mg_collect_error(mech, maj_stat, min_stat); return GSS_S_BAD_MECH; } gss_release_buffer(&min_stat, &out); gss_delete_sec_context(&min_stat, &ctx, NULL); return GSS_S_COMPLETE; }
Ssh_gss_stat ssh_gss_init_sec_context(Ssh_gss_ctx *ctx, Ssh_gss_name srv_name, int to_deleg, Ssh_gss_buf *recv_tok, Ssh_gss_buf *send_tok) { uxSsh_gss_ctx *uxctx = (uxSsh_gss_ctx*) *ctx; OM_uint32 ret_flags; if (to_deleg) to_deleg = GSS_C_DELEG_FLAG; uxctx->maj_stat = gss_init_sec_context(&uxctx->min_stat, GSS_C_NO_CREDENTIAL, &uxctx->ctx, srv_name, (gss_OID) putty_gss_mech_krb5, GSS_C_MUTUAL_FLAG | GSS_C_INTEG_FLAG | to_deleg, 0, GSS_C_NO_CHANNEL_BINDINGS, recv_tok, NULL, /* ignore mech type */ send_tok, &ret_flags, NULL); /* ignore time_rec */ if (uxctx->maj_stat == GSS_S_COMPLETE) return SSH_GSS_S_COMPLETE; if (uxctx->maj_stat == GSS_S_CONTINUE_NEEDED) return SSH_GSS_S_CONTINUE_NEEDED; return SSH_GSS_FAILURE; }
/* * Wrapper to init_sec_context. Requires that the context contains: * * oid * server name (from ssh_gssapi_import_name) */ OM_uint32 ssh_gssapi_init_ctx(Gssctxt *ctx, const char *server_host, int deleg_creds, gss_buffer_t recv_tok, gss_buffer_t send_tok) { int flags = GSS_C_MUTUAL_FLAG | GSS_C_INTEG_FLAG; debug("%s(%p, %s, %d, %p, %p)", __func__, ctx, server_host, deleg_creds, recv_tok, send_tok); if (deleg_creds) { flags |= GSS_C_DELEG_FLAG; debug("Delegating GSS-API credentials"); } /* Build target principal */ if (ctx->desired_name == GSS_C_NO_NAME && !ssh_gssapi_import_name(ctx, server_host)) { return (ctx->major); } ctx->major = gss_init_sec_context(&ctx->minor, GSS_C_NO_CREDENTIAL, &ctx->context, ctx->desired_name, ctx->desired_mech, flags, 0, /* default lifetime */ NULL, /* no channel bindings */ recv_tok, NULL, /* actual mech type */ send_tok, &ctx->flags, NULL); /* actual lifetime */ if (GSS_ERROR(ctx->major)) ssh_gssapi_error(ctx, "calling GSS_Init_sec_context()"); return (ctx->major); }
static int write_one_token(gss_name_t service, int delegate, int negotiate) { gss_ctx_id_t ctx = GSS_C_NO_CONTEXT; gss_buffer_desc in; gss_buffer_desc out; OM_uint32 maj; OM_uint32 min; OM_uint32 flags = 0; int ret = 0; char *base64_output = NULL; in.length = 0; in.value = 0; out.length = 0; out.value = 0; if (delegate) flags |= GSS_C_DELEG_FLAG; maj = gss_init_sec_context(&min, GSS_C_NO_CREDENTIAL, &ctx, service, GSS_C_NO_OID, flags, 0, GSS_C_NO_CHANNEL_BINDINGS, &in, NULL, &out, NULL, NULL); GBAIL("gss_init_sec_context", maj, min); base64_output = base64_encode(out.value, out.length); if (!base64_output) { fprintf(stderr, "Out of memory.\n"); ret = 1; goto bail; } if (!nflag) printf("%s%s\n", negotiate?"Negotiate ":"", base64_output); bail: if (out.value) gss_release_buffer(&min, &out); if (ctx != GSS_C_NO_CONTEXT) { /* * XXXrcd: here we ignore the fact that we might have an * output token as this program doesn't do terribly * well in that case. */ gss_delete_sec_context(&min, &ctx, NULL); } free(base64_output); return ret; }
static gint soup_gss_client_step (SoupNegotiateConnectionState *conn, const gchar *challenge, GError **err) { OM_uint32 maj_stat, min_stat; gss_buffer_desc in = GSS_C_EMPTY_BUFFER; gss_buffer_desc out = GSS_C_EMPTY_BUFFER; gint ret = AUTH_GSS_CONTINUE; g_clear_pointer (&conn->response_header, g_free); if (challenge && *challenge) { size_t len; in.value = g_base64_decode (challenge, &len); in.length = len; } maj_stat = gss_init_sec_context (&min_stat, GSS_C_NO_CREDENTIAL, &conn->context, conn->server_name, (gss_OID) &gss_mech_spnego, GSS_C_MUTUAL_FLAG, GSS_C_INDEFINITE, GSS_C_NO_CHANNEL_BINDINGS, &in, NULL, &out, NULL, NULL); if ((maj_stat != GSS_S_COMPLETE) && (maj_stat != GSS_S_CONTINUE_NEEDED)) { soup_gss_error (maj_stat, min_stat, err); ret = AUTH_GSS_ERROR; goto out; } ret = (maj_stat == GSS_S_COMPLETE) ? AUTH_GSS_COMPLETE : AUTH_GSS_CONTINUE; if (out.length) { gchar *response = g_base64_encode ((const guchar *) out.value, out.length); conn->response_header = g_strconcat ("Negotiate ", response, NULL); g_free (response); maj_stat = gss_release_buffer (&min_stat, &out); } out: if (out.value) gss_release_buffer (&min_stat, &out); if (in.value) g_free (in.value); return ret; }
void initGSSAPIContext( const char * inServiceName, gss_ctx_id_t * outContext, gss_buffer_desc * outToken, PAPIStatus * outStatus ) { gss_name_t theServiceName; getTargetService( inServiceName, &theServiceName, outStatus ); if ( *outStatus == PAPISuccess ) { OM_uint32 theMajorStatus; OM_uint32 theMinorStatus; gss_buffer_desc theServiceNameBuf; OM_uint32 theReqFlags = GSS_C_DELEG_FLAG; OM_uint32 theRetFlags; gss_OID theMech; #ifdef SOLARIS gss_OID_desc theMechArr = { 9, "\052\206\110\206\367\022\001\002\002"}; theMech = &theMechArr; #else theMech = GSS_KRB5_MECHANISM; #endif outToken->length = 0; theMajorStatus = gss_init_sec_context( &theMinorStatus, GSS_C_NO_CREDENTIAL, outContext, theServiceName, GSS_C_NO_OID, 0, 0, NULL, GSS_C_NO_BUFFER, 0, outToken, &theRetFlags, 0 ); if ( theMajorStatus == GSS_S_COMPLETE ) { *outStatus = PAPISuccess; } else { *outStatus = PAPIAuthenticationFailure; } } }
uint32_t sapgss_init_sec_context( uint32_t *minor_status, gss_cred_id_t claimant_cred_handle, gss_ctx_id_t *context_handle, gss_name_t target_name, sapgss_OID mech_type, uint32_t req_flags, uint32_t time_req, sapgss_channel_bindings_t input_chan_bindings, gss_buffer_t input_token, sapgss_OID *actual_mech_type, gss_buffer_t output_token, uint32_t *ret_flags, uint32_t *time_rec) { gss_OID mech_type_loc, actual_mech_type_loc; uint32_t major_status; int ret; memset(&mech_type_loc, 0, sizeof(mech_type_loc)); actual_mech_type_loc = NULL; /* Hope nobody uses these */ if (input_chan_bindings != NULL) return GSS_S_FAILURE; ret = gss_OID_sap_to_loc(mech_type, &mech_type_loc); if (ret != 0) { *minor_status = ret; return GSS_S_FAILURE; } major_status = gss_init_sec_context(minor_status, claimant_cred_handle, context_handle, target_name, mech_type_loc, req_flags, time_req, NULL, input_token, &actual_mech_type_loc, output_token, ret_flags, time_rec); /* Comply with the gss_OID_sap_to_loc contract and free mech_type_loc */ gss_OID_loc_release(&mech_type_loc); ret = gss_OID_loc_to_sap(actual_mech_type_loc, actual_mech_type); if (ret != 0) { *minor_status = ret; return GSS_S_FAILURE; } return major_status; }
static int init_user(gss_ctx_id_t *ctx, const char *service, const char *hostname, gss_buffer_desc *input_token, gss_buffer_desc *output_token) { OM_uint32 maj_stat, min_stat; gss_buffer_desc name_token; gss_name_t server; const gss_OID mech_oid = GSS_C_NO_OID; memset(&name_token, 0, sizeof(name_token)); name_token.length = asprintf ((char **)&name_token.value, "%s@%s", service, hostname); maj_stat = gss_import_name (&min_stat, &name_token, GSS_C_NT_HOSTBASED_SERVICE, &server); if (GSS_ERROR(maj_stat)) gss_err (1, min_stat, "Error importing name `%s@%s':\n", service, hostname); maj_stat = gss_init_sec_context(&min_stat, GSS_C_NO_CREDENTIAL, ctx, server, mech_oid, GSS_C_DELEG_FLAG, 0, GSS_C_NO_CHANNEL_BINDINGS, input_token, NULL, output_token, NULL, NULL); if (GSS_ERROR(maj_stat)) gss_err (1, min_stat, "gss_init_sec_context"); return maj_stat; }
/* this function returns 1, if an event is added */ static int gssInitiateSecurityContextNext( struct gfarmGssInitiateSecurityContextState *state) { OM_uint32 minStat2; int rv; static const char diag[] = "gssInitiateSecurityContextNext()"; gfarm_mutex_lock(&gss_mutex, diag, gssDiag); state->majStat = gss_init_sec_context(&state->minStat, state->cred, &state->sc, state->acceptorName, GSS_C_NO_OID, state->reqFlag, 0, GSS_C_NO_CHANNEL_BINDINGS, state->itPtr, state->actualMechType, state->otPtr, &state->retFlag, &state->timeRet); gfarm_mutex_unlock(&gss_mutex, diag, gssDiag); if (state->itPtr->length > 0) gss_release_buffer(&minStat2, state->itPtr); if (state->otPtr->length > 0) { rv = gfarm_eventqueue_add_event(state->q, state->writable, NULL); if (rv == 0) { /* go to gfarmGssInitiateSecurityContextSendToken() */ return 1; } gflog_auth_error(GFARM_MSG_1000622, "gfarm:gssInitiateSecurityContextNext(): %s", strerror(rv)); state->majStat = GSS_S_FAILURE; state->minStat = GFSL_DEFAULT_MINOR_ERROR; } return gssInitiateSecurityContextSwitch(state); }
/* * Wrapper to init_sec_context * Requires that the context contains: * oid * server name (from ssh_gssapi_import_name) */ OM_uint32 ssh_gssapi_init_ctx(Gssctxt *ctx, int deleg_creds, gss_buffer_desc *recv_tok, gss_buffer_desc* send_tok, OM_uint32 *flags) { int deleg_flag = 0; if (deleg_creds) { deleg_flag = GSS_C_DELEG_FLAG; debug("Delegating credentials"); } ctx->major = gss_init_sec_context(&ctx->minor, GSS_C_NO_CREDENTIAL, &ctx->context, ctx->name, ctx->oid, GSS_C_MUTUAL_FLAG | GSS_C_INTEG_FLAG | deleg_flag, 0, NULL, recv_tok, NULL, send_tok, flags, NULL); if (GSS_ERROR(ctx->major)) ssh_gssapi_error(ctx); return (ctx->major); }
OM_uint32 Curl_gss_init_sec_context( struct SessionHandle *data, OM_uint32 *minor_status, gss_ctx_id_t *context, gss_name_t target_name, gss_OID mech_type, gss_channel_bindings_t input_chan_bindings, gss_buffer_t input_token, gss_buffer_t output_token, OM_uint32 *ret_flags) { OM_uint32 req_flags = GSS_C_MUTUAL_FLAG | GSS_C_REPLAY_FLAG; if(data->set.gssapi_delegation & CURLGSSAPI_DELEGATION_POLICY_FLAG) { #ifdef GSS_C_DELEG_POLICY_FLAG req_flags |= GSS_C_DELEG_POLICY_FLAG; #else infof(data, "warning: support for CURLGSSAPI_DELEGATION_POLICY_FLAG not " "compiled in\n"); #endif } if(data->set.gssapi_delegation & CURLGSSAPI_DELEGATION_FLAG) req_flags |= GSS_C_DELEG_FLAG; return gss_init_sec_context(minor_status, GSS_C_NO_CREDENTIAL, /* cred_handle */ context, target_name, mech_type, req_flags, 0, /* time_req */ input_chan_bindings, input_token, NULL, /* actual_mech_type */ output_token, ret_flags, NULL /* time_rec */); }
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); }
int zmq::gssapi_client_t::initialize_context () { // First time through, import service_name into target_name if (target_name == GSS_C_NO_NAME) { send_tok.value = service_name; send_tok.length = strlen(service_name); OM_uint32 maj = gss_import_name(&min_stat, &send_tok, GSS_C_NT_HOSTBASED_SERVICE, &target_name); if (maj != GSS_S_COMPLETE) return -1; } maj_stat = gss_init_sec_context(&init_sec_min_stat, cred, &context, target_name, mechs.elements, gss_flags, 0, NULL, token_ptr, NULL, &send_tok, &ret_flags, NULL); if (token_ptr != GSS_C_NO_BUFFER) free(recv_tok.value); return 0; }
static int krb5_auth(void *app_data, struct connectdata *conn) { int ret; char *p; const char *host = conn->dns_entry->addr->ai_canonname; ssize_t nread; curl_socklen_t l = sizeof(conn->local_addr); struct SessionHandle *data = conn->data; CURLcode result; const char *service = "ftp", *srv_host = "host"; gss_buffer_desc gssbuf, _gssresp, *gssresp; OM_uint32 maj, min; gss_name_t gssname; gss_ctx_id_t *context = app_data; struct gss_channel_bindings_struct chan; if(getsockname(conn->sock[FIRSTSOCKET], (struct sockaddr *)LOCAL_ADDR, &l) < 0) perror("getsockname()"); chan.initiator_addrtype = GSS_C_AF_INET; chan.initiator_address.length = l - 4; chan.initiator_address.value = &((struct sockaddr_in *)LOCAL_ADDR)->sin_addr.s_addr; chan.acceptor_addrtype = GSS_C_AF_INET; chan.acceptor_address.length = l - 4; chan.acceptor_address.value = &((struct sockaddr_in *)REMOTE_ADDR)->sin_addr.s_addr; chan.application_data.length = 0; chan.application_data.value = NULL; /* this loop will execute twice (once for service, once for host) */ while(1) { /* this really shouldn't be repeated here, but can't help it */ if(service == srv_host) { result = Curl_ftpsendf(conn, "AUTH GSSAPI"); if(result) return -2; if(Curl_GetFTPResponse(&nread, conn, NULL)) return -1; if(data->state.buffer[0] != '3') return -1; } gssbuf.value = data->state.buffer; gssbuf.length = snprintf(gssbuf.value, BUFSIZE, "%s@%s", service, host); maj = gss_import_name(&min, &gssbuf, GSS_C_NT_HOSTBASED_SERVICE, &gssname); if(maj != GSS_S_COMPLETE) { gss_release_name(&min, &gssname); if(service == srv_host) { Curl_failf(data, "Error importing service name %s", gssbuf.value); return AUTH_ERROR; } service = srv_host; continue; } { gss_OID t; gss_display_name(&min, gssname, &gssbuf, &t); Curl_infof(data, "Trying against %s\n", gssbuf.value); gss_release_buffer(&min, &gssbuf); } gssresp = GSS_C_NO_BUFFER; *context = GSS_C_NO_CONTEXT; do { ret = AUTH_OK; maj = gss_init_sec_context(&min, GSS_C_NO_CREDENTIAL, context, gssname, GSS_C_NO_OID, GSS_C_MUTUAL_FLAG | GSS_C_REPLAY_FLAG, 0, &chan, gssresp, NULL, &gssbuf, NULL, NULL); if(gssresp) { free(_gssresp.value); gssresp = NULL; } if(maj != GSS_S_COMPLETE && maj != GSS_S_CONTINUE_NEEDED) { Curl_infof(data, "Error creating security context"); ret = AUTH_ERROR; break; } if(gssbuf.length != 0) { if(Curl_base64_encode(data, (char *)gssbuf.value, gssbuf.length, &p) < 1) { Curl_infof(data, "Out of memory base64-encoding"); ret = AUTH_CONTINUE; break; } result = Curl_ftpsendf(conn, "ADAT %s", p); free(p); if(result) { ret = -2; break; } if(Curl_GetFTPResponse(&nread, conn, NULL)) { ret = -1; break; } if(data->state.buffer[0] != '2' && data->state.buffer[0] != '3'){ Curl_infof(data, "Server didn't accept auth data\n"); ret = AUTH_ERROR; break; } p = data->state.buffer + 4; p = strstr(p, "ADAT="); if(p) { _gssresp.length = Curl_base64_decode(p + 5, (unsigned char **) &_gssresp.value); if(_gssresp.length < 1) { Curl_failf(data, "Out of memory base64-encoding"); ret = AUTH_CONTINUE; break; } } gssresp = &_gssresp; } } while(maj == GSS_S_CONTINUE_NEEDED); gss_release_name(&min, &gssname); if(gssresp) free(_gssresp.value); if(ret == AUTH_OK || service == srv_host) return ret; service = srv_host; } }
static OM_uint32 spnego_initial (OM_uint32 * minor_status, gssspnego_cred cred, gss_ctx_id_t * context_handle, const gss_name_t target_name, const gss_OID mech_type, OM_uint32 req_flags, OM_uint32 time_req, const gss_channel_bindings_t input_chan_bindings, const gss_buffer_t input_token, gss_OID * actual_mech_type, gss_buffer_t output_token, OM_uint32 * ret_flags, OM_uint32 * time_rec ) { NegTokenInit ni; int ret; OM_uint32 sub, minor; gss_buffer_desc mech_token; u_char *buf; size_t buf_size, buf_len; gss_buffer_desc data; size_t ni_len; gss_ctx_id_t context; gssspnego_ctx ctx; spnego_name name = (spnego_name)target_name; *minor_status = 0; memset (&ni, 0, sizeof(ni)); *context_handle = GSS_C_NO_CONTEXT; if (target_name == GSS_C_NO_NAME) return GSS_S_BAD_NAME; sub = _gss_spnego_alloc_sec_context(&minor, &context); if (GSS_ERROR(sub)) { *minor_status = minor; return sub; } ctx = (gssspnego_ctx)context; HEIMDAL_MUTEX_lock(&ctx->ctx_id_mutex); ctx->local = 1; sub = gss_import_name(&minor, &name->value, &name->type, &ctx->target_name); if (GSS_ERROR(sub)) { *minor_status = minor; _gss_spnego_internal_delete_sec_context(&minor, &context, GSS_C_NO_BUFFER); return sub; } sub = _gss_spnego_indicate_mechtypelist(&minor, ctx->target_name, initiator_approved, 0, cred, &ni.mechTypes, &ctx->preferred_mech_type); if (GSS_ERROR(sub)) { *minor_status = minor; _gss_spnego_internal_delete_sec_context(&minor, &context, GSS_C_NO_BUFFER); return sub; } ni.reqFlags = NULL; /* * If we have a credential handle, use it to select the mechanism * that we will use */ /* generate optimistic token */ sub = gss_init_sec_context(&minor, (cred != NULL) ? cred->negotiated_cred_id : GSS_C_NO_CREDENTIAL, &ctx->negotiated_ctx_id, ctx->target_name, ctx->preferred_mech_type, req_flags, time_req, input_chan_bindings, input_token, &ctx->negotiated_mech_type, &mech_token, &ctx->mech_flags, &ctx->mech_time_rec); if (GSS_ERROR(sub)) { free_NegTokenInit(&ni); *minor_status = minor; gss_mg_collect_error(ctx->preferred_mech_type, sub, minor); _gss_spnego_internal_delete_sec_context(&minor, &context, GSS_C_NO_BUFFER); return sub; } if (sub == GSS_S_COMPLETE) ctx->maybe_open = 1; if (mech_token.length != 0) { ALLOC(ni.mechToken, 1); if (ni.mechToken == NULL) { free_NegTokenInit(&ni); gss_release_buffer(&minor, &mech_token); _gss_spnego_internal_delete_sec_context(&minor, &context, GSS_C_NO_BUFFER); *minor_status = ENOMEM; return GSS_S_FAILURE; } ni.mechToken->length = mech_token.length; ni.mechToken->data = malloc(mech_token.length); if (ni.mechToken->data == NULL && mech_token.length != 0) { free_NegTokenInit(&ni); gss_release_buffer(&minor, &mech_token); *minor_status = ENOMEM; _gss_spnego_internal_delete_sec_context(&minor, &context, GSS_C_NO_BUFFER); return GSS_S_FAILURE; } memcpy(ni.mechToken->data, mech_token.value, mech_token.length); gss_release_buffer(&minor, &mech_token); } else ni.mechToken = NULL; ni.mechListMIC = NULL; ni_len = length_NegTokenInit(&ni); buf_size = 1 + der_length_len(ni_len) + ni_len; buf = malloc(buf_size); if (buf == NULL) { free_NegTokenInit(&ni); *minor_status = ENOMEM; _gss_spnego_internal_delete_sec_context(&minor, &context, GSS_C_NO_BUFFER); return GSS_S_FAILURE; } ret = encode_NegTokenInit(buf + buf_size - 1, ni_len, &ni, &buf_len); if (ret == 0 && ni_len != buf_len) abort(); if (ret == 0) { size_t tmp; ret = der_put_length_and_tag(buf + buf_size - buf_len - 1, buf_size - buf_len, buf_len, ASN1_C_CONTEXT, CONS, 0, &tmp); if (ret == 0 && tmp + buf_len != buf_size) abort(); } if (ret) { *minor_status = ret; free(buf); free_NegTokenInit(&ni); _gss_spnego_internal_delete_sec_context(&minor, &context, GSS_C_NO_BUFFER); return GSS_S_FAILURE; } data.value = buf; data.length = buf_size; ctx->initiator_mech_types.len = ni.mechTypes.len; ctx->initiator_mech_types.val = ni.mechTypes.val; ni.mechTypes.len = 0; ni.mechTypes.val = NULL; free_NegTokenInit(&ni); sub = gss_encapsulate_token(&data, GSS_SPNEGO_MECHANISM, output_token); free (buf); if (sub) { _gss_spnego_internal_delete_sec_context(&minor, &context, GSS_C_NO_BUFFER); return sub; } if (actual_mech_type) *actual_mech_type = ctx->negotiated_mech_type; if (ret_flags) *ret_flags = ctx->mech_flags; if (time_rec) *time_rec = ctx->mech_time_rec; HEIMDAL_MUTEX_unlock(&ctx->ctx_id_mutex); *context_handle = context; return GSS_S_CONTINUE_NEEDED; }
static OM_uint32 spnego_reply (OM_uint32 * minor_status, const gssspnego_cred cred, gss_ctx_id_t * context_handle, const gss_name_t target_name, const gss_OID mech_type, OM_uint32 req_flags, OM_uint32 time_req, const gss_channel_bindings_t input_chan_bindings, const gss_buffer_t input_token, gss_OID * actual_mech_type, gss_buffer_t output_token, OM_uint32 * ret_flags, OM_uint32 * time_rec ) { OM_uint32 ret, minor; NegotiationToken resp; gss_OID_desc mech; int require_mic; size_t buf_len; gss_buffer_desc mic_buf, mech_buf; gss_buffer_desc mech_output_token; gssspnego_ctx ctx; *minor_status = 0; ctx = (gssspnego_ctx)*context_handle; output_token->length = 0; output_token->value = NULL; mech_output_token.length = 0; mech_output_token.value = NULL; mech_buf.value = NULL; mech_buf.length = 0; ret = decode_NegotiationToken(input_token->value, input_token->length, &resp, NULL); if (ret) return ret; if (resp.element != choice_NegotiationToken_negTokenResp) { free_NegotiationToken(&resp); *minor_status = 0; return GSS_S_BAD_MECH; } if (resp.u.negTokenResp.negResult == NULL || *(resp.u.negTokenResp.negResult) == reject /* || resp.u.negTokenResp.supportedMech == NULL */ ) { free_NegotiationToken(&resp); return GSS_S_BAD_MECH; } /* * Pick up the mechanism that the acceptor selected, only allow it * to be sent in packet. */ HEIMDAL_MUTEX_lock(&ctx->ctx_id_mutex); if (resp.u.negTokenResp.supportedMech) { if (ctx->oidlen) { free_NegotiationToken(&resp); HEIMDAL_MUTEX_unlock(&ctx->ctx_id_mutex); return GSS_S_BAD_MECH; } ret = der_put_oid(ctx->oidbuf + sizeof(ctx->oidbuf) - 1, sizeof(ctx->oidbuf), resp.u.negTokenResp.supportedMech, &ctx->oidlen); /* Avoid recursively embedded SPNEGO */ if (ret || (ctx->oidlen == GSS_SPNEGO_MECHANISM->length && memcmp(ctx->oidbuf + sizeof(ctx->oidbuf) - ctx->oidlen, GSS_SPNEGO_MECHANISM->elements, ctx->oidlen) == 0)) { free_NegotiationToken(&resp); HEIMDAL_MUTEX_unlock(&ctx->ctx_id_mutex); return GSS_S_BAD_MECH; } /* check if the acceptor took our optimistic token */ if (ctx->oidlen != ctx->preferred_mech_type->length || memcmp(ctx->oidbuf + sizeof(ctx->oidbuf) - ctx->oidlen, ctx->preferred_mech_type->elements, ctx->oidlen) != 0) { gss_delete_sec_context(&minor, &ctx->negotiated_ctx_id, GSS_C_NO_BUFFER); ctx->negotiated_ctx_id = GSS_C_NO_CONTEXT; } } else if (ctx->oidlen == 0) { free_NegotiationToken(&resp); HEIMDAL_MUTEX_unlock(&ctx->ctx_id_mutex); return GSS_S_BAD_MECH; } /* if a token (of non zero length), or no context, pass to underlaying mech */ if ((resp.u.negTokenResp.responseToken != NULL && resp.u.negTokenResp.responseToken->length) || ctx->negotiated_ctx_id == GSS_C_NO_CONTEXT) { gss_buffer_desc mech_input_token; if (resp.u.negTokenResp.responseToken) { mech_input_token.length = resp.u.negTokenResp.responseToken->length; mech_input_token.value = resp.u.negTokenResp.responseToken->data; } else { mech_input_token.length = 0; mech_input_token.value = NULL; } mech.length = ctx->oidlen; mech.elements = ctx->oidbuf + sizeof(ctx->oidbuf) - ctx->oidlen; /* Fall through as if the negotiated mechanism was requested explicitly */ ret = gss_init_sec_context(&minor, (cred != NULL) ? cred->negotiated_cred_id : GSS_C_NO_CREDENTIAL, &ctx->negotiated_ctx_id, ctx->target_name, &mech, req_flags, time_req, input_chan_bindings, &mech_input_token, &ctx->negotiated_mech_type, &mech_output_token, &ctx->mech_flags, &ctx->mech_time_rec); if (GSS_ERROR(ret)) { HEIMDAL_MUTEX_unlock(&ctx->ctx_id_mutex); free_NegotiationToken(&resp); gss_mg_collect_error(&mech, ret, minor); *minor_status = minor; return ret; } if (ret == GSS_S_COMPLETE) { ctx->open = 1; } } else if (*(resp.u.negTokenResp.negResult) == accept_completed) { if (ctx->maybe_open) ctx->open = 1; } if (*(resp.u.negTokenResp.negResult) == request_mic) { ctx->require_mic = 1; } if (ctx->open) { /* * Verify the mechListMIC if one was provided or CFX was * used and a non-preferred mechanism was selected */ if (resp.u.negTokenResp.mechListMIC != NULL) { require_mic = 1; } else { ret = _gss_spnego_require_mechlist_mic(minor_status, ctx, &require_mic); if (ret) { HEIMDAL_MUTEX_unlock(&ctx->ctx_id_mutex); free_NegotiationToken(&resp); gss_release_buffer(&minor, &mech_output_token); return ret; } } } else { require_mic = 0; } if (require_mic) { ASN1_MALLOC_ENCODE(MechTypeList, mech_buf.value, mech_buf.length, &ctx->initiator_mech_types, &buf_len, ret); if (ret) { HEIMDAL_MUTEX_unlock(&ctx->ctx_id_mutex); free_NegotiationToken(&resp); gss_release_buffer(&minor, &mech_output_token); *minor_status = ret; return GSS_S_FAILURE; } if (mech_buf.length != buf_len) abort(); if (resp.u.negTokenResp.mechListMIC == NULL) { HEIMDAL_MUTEX_unlock(&ctx->ctx_id_mutex); free(mech_buf.value); free_NegotiationToken(&resp); *minor_status = 0; return GSS_S_DEFECTIVE_TOKEN; } mic_buf.length = resp.u.negTokenResp.mechListMIC->length; mic_buf.value = resp.u.negTokenResp.mechListMIC->data; if (mech_output_token.length == 0) { ret = gss_verify_mic(minor_status, ctx->negotiated_ctx_id, &mech_buf, &mic_buf, NULL); if (ret) { HEIMDAL_MUTEX_unlock(&ctx->ctx_id_mutex); free(mech_buf.value); gss_release_buffer(&minor, &mech_output_token); free_NegotiationToken(&resp); return GSS_S_DEFECTIVE_TOKEN; } ctx->verified_mic = 1; } } ret = spnego_reply_internal(minor_status, ctx, require_mic ? &mech_buf : NULL, &mech_output_token, output_token); if (mech_buf.value != NULL) free(mech_buf.value); free_NegotiationToken(&resp); gss_release_buffer(&minor, &mech_output_token); if (actual_mech_type) *actual_mech_type = ctx->negotiated_mech_type; if (ret_flags) *ret_flags = ctx->mech_flags; if (time_rec) *time_rec = ctx->mech_time_rec; HEIMDAL_MUTEX_unlock(&ctx->ctx_id_mutex); return ret; }
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; }
/* * Continue GSS authentication with next token as needed. */ static int pg_GSS_continue(PGconn *conn) { OM_uint32 maj_stat, min_stat, lmin_s; maj_stat = gss_init_sec_context(&min_stat, GSS_C_NO_CREDENTIAL, &conn->gctx, conn->gtarg_nam, GSS_C_NO_OID, GSS_C_MUTUAL_FLAG, 0, GSS_C_NO_CHANNEL_BINDINGS, (conn->gctx == GSS_C_NO_CONTEXT) ? GSS_C_NO_BUFFER : &conn->ginbuf, NULL, &conn->goutbuf, NULL, NULL); if (conn->gctx != GSS_C_NO_CONTEXT) { free(conn->ginbuf.value); conn->ginbuf.value = NULL; conn->ginbuf.length = 0; } if (conn->goutbuf.length != 0) { /* * GSS generated data to send to the server. We don't care if it's the * first or subsequent packet, just send the same kind of password * packet. */ if (pqPacketSend(conn, 'p', conn->goutbuf.value, conn->goutbuf.length) != STATUS_OK) { gss_release_buffer(&lmin_s, &conn->goutbuf); return STATUS_ERROR; } } gss_release_buffer(&lmin_s, &conn->goutbuf); if (maj_stat != GSS_S_COMPLETE && maj_stat != GSS_S_CONTINUE_NEEDED) { pg_GSS_error(libpq_gettext("GSSAPI continuation error"), conn, maj_stat, min_stat); gss_release_name(&lmin_s, &conn->gtarg_nam); if (conn->gctx) gss_delete_sec_context(&lmin_s, &conn->gctx, GSS_C_NO_BUFFER); return STATUS_ERROR; } if (maj_stat == GSS_S_COMPLETE) gss_release_name(&lmin_s, &conn->gtarg_nam); return STATUS_OK; }
/* this performs a SASL/gssapi bind we avoid using cyrus-sasl to make Samba more robust. cyrus-sasl is very dependent on correctly configured DNS whereas this routine is much less fragile see RFC2078 and RFC2222 for details */ static ADS_STATUS ads_sasl_gssapi_do_bind(ADS_STRUCT *ads, const gss_name_t serv_name) { uint32_t minor_status; gss_cred_id_t gss_cred = GSS_C_NO_CREDENTIAL; gss_ctx_id_t context_handle = GSS_C_NO_CONTEXT; gss_OID mech_type = GSS_C_NULL_OID; gss_buffer_desc output_token, input_token; uint32_t req_flags, ret_flags; int conf_state; struct berval cred; struct berval *scred = NULL; int i=0; int gss_rc, rc; uint8_t *p; uint32_t max_msg_size = ADS_SASL_WRAPPING_OUT_MAX_WRAPPED; uint8_t wrap_type = ADS_SASLWRAP_TYPE_PLAIN; ADS_STATUS status; struct ads_saslwrap *wrap = &ads->ldap_wrap_data; input_token.value = NULL; input_token.length = 0; status = ads_init_gssapi_cred(ads, &gss_cred); if (!ADS_ERR_OK(status)) { goto failed; } /* * Note: here we always ask the gssapi for sign and seal * as this is negotiated later after the mutal * authentication */ req_flags = GSS_C_MUTUAL_FLAG | GSS_C_REPLAY_FLAG | GSS_C_INTEG_FLAG | GSS_C_CONF_FLAG; for (i=0; i < MAX_GSS_PASSES; i++) { gss_rc = gss_init_sec_context(&minor_status, gss_cred, &context_handle, serv_name, mech_type, req_flags, 0, NULL, &input_token, NULL, &output_token, &ret_flags, NULL); if (scred) { ber_bvfree(scred); scred = NULL; } if (gss_rc && gss_rc != GSS_S_CONTINUE_NEEDED) { status = ADS_ERROR_GSS(gss_rc, minor_status); goto failed; } cred.bv_val = (char *)output_token.value; cred.bv_len = output_token.length; rc = ldap_sasl_bind_s(ads->ldap.ld, NULL, "GSSAPI", &cred, NULL, NULL, &scred); if (rc != LDAP_SASL_BIND_IN_PROGRESS) { status = ADS_ERROR(rc); goto failed; } if (output_token.value) { gss_release_buffer(&minor_status, &output_token); } if (scred) { input_token.value = scred->bv_val; input_token.length = scred->bv_len; } else { input_token.value = NULL; input_token.length = 0; } if (gss_rc == 0) break; } gss_rc = gss_unwrap(&minor_status,context_handle,&input_token,&output_token, &conf_state,NULL); if (scred) { ber_bvfree(scred); scred = NULL; } if (gss_rc) { status = ADS_ERROR_GSS(gss_rc, minor_status); goto failed; } p = (uint8_t *)output_token.value; #if 0 file_save("sasl_gssapi.dat", output_token.value, output_token.length); #endif if (p) { wrap_type = CVAL(p,0); SCVAL(p,0,0); max_msg_size = RIVAL(p,0); } gss_release_buffer(&minor_status, &output_token); if (!(wrap_type & wrap->wrap_type)) { /* * the server doesn't supports the wrap * type we want :-( */ DEBUG(0,("The ldap sasl wrap type doesn't match wanted[%d] server[%d]\n", wrap->wrap_type, wrap_type)); DEBUGADD(0,("You may want to set the 'client ldap sasl wrapping' option\n")); status = ADS_ERROR_NT(NT_STATUS_NOT_SUPPORTED); goto failed; } /* 0x58 is the minimum windows accepts */ if (max_msg_size < 0x58) { max_msg_size = 0x58; } output_token.length = 4; output_token.value = SMB_MALLOC(output_token.length); if (!output_token.value) { output_token.length = 0; status = ADS_ERROR_NT(NT_STATUS_NO_MEMORY); goto failed; } p = (uint8_t *)output_token.value; RSIVAL(p,0,max_msg_size); SCVAL(p,0,wrap->wrap_type); /* * we used to add sprintf("dn:%s", ads->config.bind_path) here. * but using ads->config.bind_path is the wrong! It should be * the DN of the user object! * * w2k3 gives an error when we send an incorrect DN, but sending nothing * is ok and matches the information flow used in GSS-SPNEGO. */ gss_rc = gss_wrap(&minor_status, context_handle,0,GSS_C_QOP_DEFAULT, &output_token, /* used as *input* here. */ &conf_state, &input_token); /* Used as *output* here. */ if (gss_rc) { status = ADS_ERROR_GSS(gss_rc, minor_status); output_token.length = 0; SAFE_FREE(output_token.value); goto failed; } /* We've finished with output_token. */ SAFE_FREE(output_token.value); output_token.length = 0; cred.bv_val = (char *)input_token.value; cred.bv_len = input_token.length; rc = ldap_sasl_bind_s(ads->ldap.ld, NULL, "GSSAPI", &cred, NULL, NULL, &scred); gss_release_buffer(&minor_status, &input_token); status = ADS_ERROR(rc); if (!ADS_ERR_OK(status)) { goto failed; } if (wrap->wrap_type > ADS_SASLWRAP_TYPE_PLAIN) { gss_rc = gss_wrap_size_limit(&minor_status, context_handle, (wrap->wrap_type == ADS_SASLWRAP_TYPE_SEAL), GSS_C_QOP_DEFAULT, max_msg_size, &wrap->out.max_unwrapped); if (gss_rc) { status = ADS_ERROR_GSS(gss_rc, minor_status); goto failed; } wrap->out.sig_size = max_msg_size - wrap->out.max_unwrapped; wrap->in.min_wrapped = 0x2C; /* taken from a capture with LDAP unbind */ wrap->in.max_wrapped = ADS_SASL_WRAPPING_IN_MAX_WRAPPED; status = ads_setup_sasl_wrapping(wrap->wrap_private_data, ads->ldap.ld, &ads_sasl_gssapi_ops, context_handle); if (!ADS_ERR_OK(status)) { DEBUG(0, ("ads_setup_sasl_wrapping() failed: %s\n", ads_errstr(status))); goto failed; } /* make sure we don't free context_handle */ context_handle = GSS_C_NO_CONTEXT; } failed: if (gss_cred != GSS_C_NO_CREDENTIAL) gss_release_cred(&minor_status, &gss_cred); if (context_handle != GSS_C_NO_CONTEXT) gss_delete_sec_context(&minor_status, &context_handle, GSS_C_NO_BUFFER); if(scred) ber_bvfree(scred); return status; }
static DNS_ERROR dns_negotiate_gss_ctx_int( TALLOC_CTX *mem_ctx, struct dns_connection *conn, const char *keyname, const gss_name_t target_name, gss_ctx_id_t *ctx, enum dns_ServerType srv_type ) { struct gss_buffer_desc_struct input_desc, *input_ptr, output_desc; OM_uint32 major, minor; OM_uint32 ret_flags; DNS_ERROR err; gss_OID_desc krb5_oid_desc = { 9, (const char *)"\x2a\x86\x48\x86\xf7\x12\x01\x02\x02" }; *ctx = GSS_C_NO_CONTEXT; input_ptr = NULL; do { major = gss_init_sec_context( &minor, NULL, ctx, target_name, &krb5_oid_desc, GSS_C_REPLAY_FLAG | GSS_C_MUTUAL_FLAG | GSS_C_CONF_FLAG | GSS_C_INTEG_FLAG, 0, NULL, input_ptr, NULL, &output_desc, &ret_flags, NULL ); if (input_ptr != NULL) { TALLOC_FREE(input_desc.value); } if (output_desc.length != 0) { struct dns_request *req; struct dns_rrec *rec; struct dns_buffer *buf; time_t t = time(NULL); err = dns_create_query(mem_ctx, keyname, QTYPE_TKEY, DNS_CLASS_IN, &req); if (!ERR_DNS_IS_OK(err)) goto error; err = dns_create_tkey_record( req, keyname, "gss.microsoft.com", t, t + 86400, DNS_TKEY_MODE_GSSAPI, 0, output_desc.length, (uint8 *)output_desc.value, &rec ); if (!ERR_DNS_IS_OK(err)) goto error; /* Windows 2000 DNS is broken and requires the TKEY payload in the Answer section instead of the Additional seciton like Windows 2003 */ if ( srv_type == DNS_SRV_WIN2000 ) { err = dns_add_rrec(req, rec, &req->num_answers, &req->answers); } else { err = dns_add_rrec(req, rec, &req->num_additionals, &req->additionals); } if (!ERR_DNS_IS_OK(err)) goto error; err = dns_marshall_request(req, req, &buf); if (!ERR_DNS_IS_OK(err)) goto error; err = dns_send(conn, buf); if (!ERR_DNS_IS_OK(err)) goto error; TALLOC_FREE(req); } gss_release_buffer(&minor, &output_desc); if ((major != GSS_S_COMPLETE) && (major != GSS_S_CONTINUE_NEEDED)) { return ERROR_DNS_GSS_ERROR; } if (major == GSS_S_CONTINUE_NEEDED) { struct dns_request *resp; struct dns_buffer *buf; struct dns_tkey_record *tkey; err = dns_receive(mem_ctx, conn, &buf); if (!ERR_DNS_IS_OK(err)) goto error; err = dns_unmarshall_request(buf, buf, &resp); if (!ERR_DNS_IS_OK(err)) goto error; /* * TODO: Compare id and keyname */ if ((resp->num_additionals != 1) || (resp->num_answers == 0) || (resp->answers[0]->type != QTYPE_TKEY)) { err = ERROR_DNS_INVALID_MESSAGE; goto error; } err = dns_unmarshall_tkey_record( mem_ctx, resp->answers[0], &tkey); if (!ERR_DNS_IS_OK(err)) goto error; input_desc.length = tkey->key_length; input_desc.value = talloc_move(mem_ctx, &tkey->key); input_ptr = &input_desc; TALLOC_FREE(buf); } } while ( major == GSS_S_CONTINUE_NEEDED ); /* If we arrive here, we have a valid security context */ err = ERROR_DNS_SUCCESS; error: return err; }
/* Continue a GSS-API Negotiate exchange, using input TOKEN if * non-NULL. Returns non-zero on error. */ static int continue_negotiate(auth_session *sess, const char *token) { unsigned int major, minor; gss_buffer_desc input = GSS_C_EMPTY_BUFFER; gss_buffer_desc output = GSS_C_EMPTY_BUFFER; unsigned char *bintoken = NULL; int ret; if (token) { input.length = ne_unbase64(token, &bintoken); if (input.length == 0) { NE_DEBUG(NE_DBG_HTTPAUTH, "gssapi: Invalid input [%s].\n", token); return -1; } input.value = bintoken; NE_DEBUG(NE_DBG_HTTPAUTH, "gssapi: Continuation token [%s]\n", token); } else if (sess->gssctx != GSS_C_NO_CONTEXT) { NE_DEBUG(NE_DBG_HTTPAUTH, "gssapi: Reset incomplete context.\n"); gss_delete_sec_context(&minor, &sess->gssctx, GSS_C_NO_BUFFER); } major = gss_init_sec_context(&minor, GSS_C_NO_CREDENTIAL, &sess->gssctx, sess->gssname, sess->gssmech, GSS_C_MUTUAL_FLAG, GSS_C_INDEFINITE, GSS_C_NO_CHANNEL_BINDINGS, &input, &sess->gssmech, &output, NULL, NULL); /* done with the input token. */ if (bintoken) ne_free(bintoken); if (GSS_ERROR(major)) { ne_buffer *err = ne_buffer_create(); int flag = 0; make_gss_error(err, &flag, major, GSS_C_GSS_CODE); make_gss_error(err, &flag, minor, GSS_C_MECH_CODE); NE_DEBUG(NE_DBG_HTTPAUTH, "gssapi: Error: %s\n", err->data); ne_set_error(sess->sess, _("GSSAPI authentication error (%s)"), err->data); ne_buffer_destroy(err); return -1; } if (major == GSS_S_CONTINUE_NEEDED || major == GSS_S_COMPLETE) { NE_DEBUG(NE_DBG_HTTPAUTH, "gssapi: init_sec_context OK. (major=%d)\n", major); ret = 0; } else { NE_DEBUG(NE_DBG_HTTPAUTH, "gssapi: Init failure %d.\n", major); ret = -1; } if (major != GSS_S_CONTINUE_NEEDED) { /* context no longer needed: destroy it */ gss_delete_sec_context(&minor, &sess->gssctx, GSS_C_NO_BUFFER); } if (output.length) { sess->gssapi_token = ne_base64(output.value, output.length); NE_DEBUG(NE_DBG_HTTPAUTH, "gssapi: Output token: [%s]\n", sess->gssapi_token); gss_release_buffer(&minor, &output); } else { NE_DEBUG(NE_DBG_HTTPAUTH, "gssapi: No output token.\n"); } return ret; }
int Curl_input_negotiate(struct connectdata *conn, char *header) { struct negotiatedata *neg_ctx = &conn->data->state.negotiate; OM_uint32 major_status, minor_status, minor_status2; gss_buffer_desc input_token = GSS_C_EMPTY_BUFFER; gss_buffer_desc output_token = GSS_C_EMPTY_BUFFER; int ret; size_t len; bool gss; const char* protocol; while(*header && ISSPACE(*header)) header++; if(checkprefix("GSS-Negotiate", header)) { protocol = "GSS-Negotiate"; gss = TRUE; } else if (checkprefix("Negotiate", header)) { protocol = "Negotiate"; gss = FALSE; } else return -1; if (neg_ctx->context) { if (neg_ctx->gss != gss) { return -1; } } else { neg_ctx->protocol = protocol; neg_ctx->gss = gss; } if (neg_ctx->context && neg_ctx->status == GSS_S_COMPLETE) { /* We finished succesfully our part of authentication, but server * rejected it (since we're again here). Exit with an error since we * can't invent anything better */ Curl_cleanup_negotiate(conn->data); return -1; } if (neg_ctx->server_name == NULL && (ret = get_gss_name(conn, &neg_ctx->server_name))) return ret; header += strlen(neg_ctx->protocol); while(*header && ISSPACE(*header)) header++; len = strlen(header); if (len > 0) { int rawlen = Curl_base64_decode(header, (unsigned char **)&input_token.value); if (rawlen < 0) return -1; input_token.length = rawlen; #ifdef HAVE_SPNEGO /* Handle SPNEGO */ if (checkprefix("Negotiate", header)) { ASN1_OBJECT * object = NULL; int rc = 1; unsigned char * spnegoToken = NULL; size_t spnegoTokenLength = 0; unsigned char * mechToken = NULL; size_t mechTokenLength = 0; spnegoToken = malloc(input_token.length); if (input_token.value == NULL) return ENOMEM; spnegoTokenLength = input_token.length; object = OBJ_txt2obj ("1.2.840.113554.1.2.2", 1); if (!parseSpnegoTargetToken(spnegoToken, spnegoTokenLength, NULL, NULL, &mechToken, &mechTokenLength, NULL, NULL)) { free(spnegoToken); spnegoToken = NULL; infof(conn->data, "Parse SPNEGO Target Token failed\n"); } else { free(input_token.value); input_token.value = NULL; input_token.value = malloc(mechTokenLength); memcpy(input_token.value, mechToken,mechTokenLength); input_token.length = mechTokenLength; free(mechToken); mechToken = NULL; infof(conn->data, "Parse SPNEGO Target Token succeeded\n"); } } #endif } major_status = gss_init_sec_context(&minor_status, GSS_C_NO_CREDENTIAL, &neg_ctx->context, neg_ctx->server_name, GSS_C_NO_OID, GSS_C_DELEG_FLAG, 0, GSS_C_NO_CHANNEL_BINDINGS, &input_token, NULL, &output_token, NULL, NULL); if (input_token.length > 0) gss_release_buffer(&minor_status2, &input_token); neg_ctx->status = major_status; if (GSS_ERROR(major_status)) { /* Curl_cleanup_negotiate(conn->data) ??? */ log_gss_error(conn, minor_status, (char *)"gss_init_sec_context() failed: "); return -1; } if (output_token.length == 0) { return -1; } neg_ctx->output_token = output_token; /* conn->bits.close = FALSE; */ return 0; }
static int gss_auth(void *app_data, char *host) { OM_uint32 maj_stat, min_stat; gss_name_t target_name; gss_buffer_desc input, output_token; int context_established = 0; char *p; int n; gss_channel_bindings_t bindings; struct gss_data *d = app_data; const char *knames[] = { "ftp", "host", NULL }, **kname = knames; if(import_name(*kname++, host, &target_name)) return AUTH_ERROR; input.length = 0; input.value = NULL; bindings = malloc(sizeof(*bindings)); sockaddr_to_gss_address (myctladdr, &bindings->initiator_addrtype, &bindings->initiator_address); sockaddr_to_gss_address (hisctladdr, &bindings->acceptor_addrtype, &bindings->acceptor_address); bindings->application_data.length = 0; bindings->application_data.value = NULL; while(!context_established) { maj_stat = gss_init_sec_context(&min_stat, GSS_C_NO_CREDENTIAL, &d->context_hdl, target_name, GSS_C_NO_OID, GSS_C_MUTUAL_FLAG | GSS_C_SEQUENCE_FLAG | GSS_C_DELEG_FLAG, 0, bindings, &input, NULL, &output_token, NULL, NULL); if (GSS_ERROR(maj_stat)) { OM_uint32 new_stat; OM_uint32 msg_ctx = 0; gss_buffer_desc status_string; if(min_stat == KRB5KDC_ERR_S_PRINCIPAL_UNKNOWN && *kname != NULL) { if(import_name(*kname++, host, &target_name)) return AUTH_ERROR; continue; } gss_display_status(&new_stat, min_stat, GSS_C_MECH_CODE, GSS_C_NO_OID, &msg_ctx, &status_string); printf("Error initializing security context: %s\n", (char*)status_string.value); gss_release_buffer(&new_stat, &status_string); return AUTH_CONTINUE; } gss_release_buffer(&min_stat, &input); if (output_token.length != 0) { base64_encode(output_token.value, output_token.length, &p); gss_release_buffer(&min_stat, &output_token); n = ftp_cmd("ADAT %s", p); free(p); } if (GSS_ERROR(maj_stat)) { if (d->context_hdl != GSS_C_NO_CONTEXT) gss_delete_sec_context (&min_stat, &d->context_hdl, GSS_C_NO_BUFFER); break; } if (maj_stat & GSS_S_CONTINUE_NEEDED) { p = strstr(ftp->reply, "ADAT="); if(p == NULL){ printf("Error: expected ADAT in reply. got: %s\n", ftp->reply); return AUTH_ERROR; } else { p+=5; input.value = malloc(strlen(p)); input.length = base64_decode(p, input.value); } } else { if(ftp->fullcode != 235) { printf("Unrecognized response code: %d\n", ftp->fullcode); return AUTH_ERROR; } context_established = 1; } } return AUTH_OK; }
int main(int argc, char **argv) { int i, s, done, print_body, gssapi_done, gssapi_started, optidx = 0; const char *host, *page; struct http_req req; char *headers[99]; /* XXX */ int num_headers; krb5_storage *sp; gss_cred_id_t client_cred = GSS_C_NO_CREDENTIAL; gss_ctx_id_t context_hdl = GSS_C_NO_CONTEXT; gss_name_t server = GSS_C_NO_NAME; gss_OID mech_oid, cred_mech_oid; OM_uint32 flags; OM_uint32 maj_stat, min_stat; setprogname(argv[0]); if(getarg(http_args, num_http_args, argc, argv, &optidx)) usage(1); if (help_flag) usage (0); if(version_flag) { print_version(NULL); exit(0); } argc -= optidx; argv += optidx; mech_oid = select_mech(mech); if (cred_mech_str) cred_mech_oid = select_mech(cred_mech_str); else cred_mech_oid = mech_oid; if (argc != 1 && argc != 2) errx(1, "usage: %s host [page]", getprogname()); host = argv[0]; if (argc == 2) page = argv[1]; else page = "/"; flags = 0; if (delegate_flag) flags |= GSS_C_DELEG_FLAG; if (policy_flag) flags |= GSS_C_DELEG_POLICY_FLAG; if (mutual_flag) flags |= GSS_C_MUTUAL_FLAG; done = 0; num_headers = 0; gssapi_done = 0; gssapi_started = 0; if (client_str) { gss_buffer_desc name_buffer; gss_name_t name; gss_OID_set mechset = GSS_C_NO_OID_SET; name_buffer.value = client_str; name_buffer.length = strlen(client_str); maj_stat = gss_import_name(&min_stat, &name_buffer, GSS_C_NT_USER_NAME, &name); if (maj_stat) errx(1, "failed to import name"); if (cred_mech_oid) { gss_create_empty_oid_set(&min_stat, &mechset); gss_add_oid_set_member(&min_stat, cred_mech_oid, &mechset); } maj_stat = gss_acquire_cred(&min_stat, name, GSS_C_INDEFINITE, mechset, GSS_C_INITIATE, &client_cred, NULL, NULL); gss_release_name(&min_stat, &name); gss_release_oid_set(&min_stat, &mechset); if (maj_stat) errx(1, "failed to find cred of name %s", client_str); } { gss_buffer_desc name_token; char *name; asprintf(&name, "%s@%s", gss_service, host); name_token.length = strlen(name); name_token.value = name; maj_stat = gss_import_name(&min_stat, &name_token, GSS_C_NT_HOSTBASED_SERVICE, &server); if (GSS_ERROR(maj_stat)) gss_err (1, min_stat, "gss_inport_name: %s", name); free(name); } s = do_connect(host, port_str); if (s < 0) errx(1, "connection failed"); sp = krb5_storage_from_fd(s); if (sp == NULL) errx(1, "krb5_storage_from_fd"); do { print_body = 0; http_query(sp, host, page, headers, num_headers, &req); for (i = 0 ; i < num_headers; i++) free(headers[i]); num_headers = 0; if (strstr(req.response, " 200 ") != NULL) { print_body = 1; done = 1; } else if (strstr(req.response, " 401 ") != NULL) { if (http_find_header(&req, "WWW-Authenticate:") == NULL) errx(1, "Got %s but missed `WWW-Authenticate'", req.response); } if (!gssapi_done) { const char *h = http_find_header(&req, "WWW-Authenticate:"); if (h == NULL) errx(1, "Got %s but missed `WWW-Authenticate'", req.response); if (strncasecmp(h, "Negotiate", 9) == 0) { gss_buffer_desc input_token, output_token; if (verbose_flag) printf("Negotiate found\n"); i = 9; while(h[i] && isspace((unsigned char)h[i])) i++; if (h[i] != '\0') { size_t len = strlen(&h[i]); int slen; if (len == 0) errx(1, "invalid Negotiate token"); input_token.value = emalloc(len); slen = base64_decode(&h[i], input_token.value); if (slen < 0) errx(1, "invalid base64 Negotiate token %s", &h[i]); input_token.length = slen; } else { if (gssapi_started) errx(1, "Negotiate already started"); gssapi_started = 1; input_token.length = 0; input_token.value = NULL; } if (strstr(req.response, " 200 ") != NULL) sleep(1); maj_stat = gss_init_sec_context(&min_stat, client_cred, &context_hdl, server, mech_oid, flags, 0, GSS_C_NO_CHANNEL_BINDINGS, &input_token, NULL, &output_token, NULL, NULL); if (maj_stat == GSS_S_CONTINUE_NEEDED) { } else if (maj_stat == GSS_S_COMPLETE) { gss_name_t targ_name, src_name; gss_buffer_desc name_buffer; gss_OID mech_type; gssapi_done = 1; maj_stat = gss_inquire_context(&min_stat, context_hdl, &src_name, &targ_name, NULL, &mech_type, NULL, NULL, NULL); if (GSS_ERROR(maj_stat)) gss_err (1, min_stat, "gss_inquire_context"); printf("Negotiate done: %s\n", mech); maj_stat = gss_display_name(&min_stat, src_name, &name_buffer, NULL); if (GSS_ERROR(maj_stat)) gss_print_errors(min_stat); else printf("Source: %.*s\n", (int)name_buffer.length, (char *)name_buffer.value); gss_release_buffer(&min_stat, &name_buffer); maj_stat = gss_display_name(&min_stat, targ_name, &name_buffer, NULL); if (GSS_ERROR(maj_stat)) gss_print_errors(min_stat); else printf("Target: %.*s\n", (int)name_buffer.length, (char *)name_buffer.value); gss_release_name(&min_stat, &targ_name); gss_release_buffer(&min_stat, &name_buffer); } else { gss_err (1, min_stat, "gss_init_sec_context"); } if (output_token.length) { char *neg_token; base64_encode(output_token.value, (int)output_token.length, &neg_token); asprintf(&headers[0], "Authorization: Negotiate %s", neg_token); num_headers = 1; free(neg_token); gss_release_buffer(&min_stat, &output_token); } if (input_token.length) free(input_token.value); } else done = 1; } else done = 1; if (print_body || verbose_flag) printf("%.*s\n", (int)req.body_size, (char *)req.body); http_req_free(&req); } while (!done); if (gssapi_done == 0) errx(1, "gssapi not done but http dance done"); krb5_storage_free(sp); close(s); return 0; }
/* * Negotiate a krb5 gss context from the client end. */ static int gss_client( struct sec_handle *rh) { struct sec_stream *rs = rh->rs; struct tcp_conn *rc = rs->rc; gss_buffer_desc send_tok, recv_tok, AA; gss_OID doid; OM_uint32 maj_stat, min_stat; unsigned int ret_flags; int rval = -1; int rvalue; gss_name_t gss_name; char *errmsg = NULL; auth_debug(1, "gss_client\n"); send_tok.value = vstralloc("host/", rs->rc->hostname, NULL); send_tok.length = strlen(send_tok.value) + 1; maj_stat = gss_import_name(&min_stat, &send_tok, GSS_C_NULL_OID, &gss_name); if (maj_stat != (OM_uint32)GSS_S_COMPLETE) { security_seterror(&rh->sech, _("can't import name %s: %s"), (char *)send_tok.value, gss_error(maj_stat, min_stat)); amfree(send_tok.value); return (-1); } amfree(send_tok.value); rc->gss_context = GSS_C_NO_CONTEXT; maj_stat = gss_display_name(&min_stat, gss_name, &AA, &doid); dbprintf(_("gss_name %s\n"), (char *)AA.value); /* * Perform the context-establishement loop. * * Every generated token is stored in send_tok which is then * transmitted to the server; every received token is stored in * recv_tok (empty on the first pass) to be processed by * the next call to gss_init_sec_context. * * GSS-API guarantees that send_tok's length will be non-zero * if and only if the server is expecting another token from us, * and that gss_init_sec_context returns GSS_S_CONTINUE_NEEDED if * and only if the server has another token to send us. */ recv_tok.value = NULL; for (recv_tok.length = 0;;) { min_stat = 0; maj_stat = gss_init_sec_context(&min_stat, GSS_C_NO_CREDENTIAL, &rc->gss_context, gss_name, GSS_C_NULL_OID, (OM_uint32)GSS_C_MUTUAL_FLAG|GSS_C_REPLAY_FLAG, 0, NULL, /* no channel bindings */ (recv_tok.length == 0 ? GSS_C_NO_BUFFER : &recv_tok), NULL, /* ignore mech type */ &send_tok, &ret_flags, NULL); /* ignore time_rec */ if (recv_tok.length != 0) { amfree(recv_tok.value); recv_tok.length = 0; } if (maj_stat != (OM_uint32)GSS_S_COMPLETE && maj_stat != (OM_uint32)GSS_S_CONTINUE_NEEDED) { security_seterror(&rh->sech, _("error getting gss context: %s %s"), gss_error(maj_stat, min_stat), (char *)send_tok.value); goto done; } /* * Send back the response */ if (send_tok.length != 0 && tcpm_send_token(rc, rc->write, rs->handle, &errmsg, send_tok.value, send_tok.length) < 0) { security_seterror(&rh->sech, "%s", rc->errmsg); gss_release_buffer(&min_stat, &send_tok); goto done; } gss_release_buffer(&min_stat, &send_tok); /* * If we need to continue, then register for more packets */ if (maj_stat != (OM_uint32)GSS_S_CONTINUE_NEEDED) break; rvalue = tcpm_recv_token(rc, rc->read, &rc->handle, &rc->errmsg, (void *)&recv_tok.value, (ssize_t *)&recv_tok.length, 60); if (rvalue <= 0) { if (rvalue < 0) security_seterror(&rh->sech, _("recv error in gss loop: %s"), rc->errmsg); else security_seterror(&rh->sech, _("EOF in gss loop")); goto done; } } rval = 0; rc->auth = 1; done: gss_release_name(&min_stat, &gss_name); return (rval); }
int gssapi_get_itoken(struct ph1handle *iph1, int *lenp) { struct gssapi_ph1_state *gps; gss_buffer_desc empty, name_token; gss_buffer_t itoken, rtoken, dummy; OM_uint32 maj_stat, min_stat; gss_name_t partner; if (gssapi_get_state(iph1) == NULL && gssapi_init(iph1) < 0) return -1; gps = gssapi_get_state(iph1); empty.length = 0; empty.value = NULL; dummy = ∅ if (iph1->approval != NULL && iph1->approval->gssid != NULL) { plog(LLV_DEBUG, LOCATION, NULL, "using provided service '%.*s'\n", iph1->approval->gssid->l, iph1->approval->gssid->v); name_token.length = iph1->approval->gssid->l; name_token.value = iph1->approval->gssid->v; maj_stat = gss_import_name(&min_stat, &name_token, GSS_C_NO_OID, &partner); if (GSS_ERROR(maj_stat)) { gssapi_error(min_stat, LOCATION, "import of %.*s\n", name_token.length, name_token.value); return -1; } } else if (gssapi_get_default_name(iph1, 1, &partner) < 0) return -1; rtoken = gps->gsscnt_p == 0 ? dummy : &gps->gss_p[gps->gsscnt_p - 1]; itoken = &gps->gss[gps->gsscnt]; gps->gss_status = gss_init_sec_context(&min_stat, gps->gss_cred, &gps->gss_context, partner, GSS_C_NO_OID, GSS_C_MUTUAL_FLAG | GSS_C_SEQUENCE_FLAG | GSS_C_CONF_FLAG | GSS_C_INTEG_FLAG, 0, GSS_C_NO_CHANNEL_BINDINGS, rtoken, NULL, itoken, NULL, NULL); if (GSS_ERROR(gps->gss_status)) { gssapi_error(min_stat, LOCATION, "init_sec_context\n"); maj_stat = gss_release_name(&min_stat, &partner); if (GSS_ERROR(maj_stat)) gssapi_error(min_stat, LOCATION, "release name\n"); return -1; } maj_stat = gss_release_name(&min_stat, &partner); if (GSS_ERROR(maj_stat)) gssapi_error(min_stat, LOCATION, "release name\n"); plog(LLV_DEBUG, LOCATION, NULL, "gss_init_sec_context status %x\n", gps->gss_status); if (lenp) *lenp = itoken->length; if (itoken->length != 0) gps->gsscnt++; return 0; }
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); }
isc_result_t dst_gssapi_initctx(dns_name_t *name, isc_buffer_t *intoken, isc_buffer_t *outtoken, gss_ctx_id_t *gssctx, isc_mem_t *mctx, char **err_message) { #ifdef GSSAPI isc_region_t r; isc_buffer_t namebuf; gss_name_t gname; OM_uint32 gret, minor, ret_flags, flags; gss_buffer_desc gintoken, *gintokenp, gouttoken = GSS_C_EMPTY_BUFFER; isc_result_t result; gss_buffer_desc gnamebuf; unsigned char array[DNS_NAME_MAXTEXT + 1]; /* Client must pass us a valid gss_ctx_id_t here */ REQUIRE(gssctx != NULL); REQUIRE(mctx != NULL); isc_buffer_init(&namebuf, array, sizeof(array)); name_to_gbuffer(name, &namebuf, &gnamebuf); /* Get the name as a GSS name */ gret = gss_import_name(&minor, &gnamebuf, GSS_C_NO_OID, &gname); if (gret != GSS_S_COMPLETE) { gss_err_message(mctx, gret, minor, err_message); result = ISC_R_FAILURE; goto out; } if (intoken != NULL) { /* Don't call gss_release_buffer for gintoken! */ REGION_TO_GBUFFER(*intoken, gintoken); gintokenp = &gintoken; } else { gintokenp = NULL; } /* * Note that we don't set GSS_C_SEQUENCE_FLAG as Windows DNS * servers don't like it. */ flags = GSS_C_REPLAY_FLAG | GSS_C_MUTUAL_FLAG | GSS_C_INTEG_FLAG; gret = gss_init_sec_context(&minor, GSS_C_NO_CREDENTIAL, gssctx, gname, GSS_SPNEGO_MECHANISM, flags, 0, NULL, gintokenp, NULL, &gouttoken, &ret_flags, NULL); if (gret != GSS_S_COMPLETE && gret != GSS_S_CONTINUE_NEEDED) { gss_err_message(mctx, gret, minor, err_message); gss_log(3, "Failure initiating security context: %s", *err_message); result = ISC_R_FAILURE; goto out; } /* * XXXSRA Not handled yet: RFC 3645 3.1.1: check ret_flags * MUTUAL and INTEG flags, fail if either not set. */ /* * RFC 2744 states the a valid output token has a non-zero length. */ if (gouttoken.length != 0) { GBUFFER_TO_REGION(gouttoken, r); RETERR(isc_buffer_copyregion(outtoken, &r)); (void)gss_release_buffer(&minor, &gouttoken); } (void)gss_release_name(&minor, &gname); if (gret == GSS_S_COMPLETE) result = ISC_R_SUCCESS; else result = DNS_R_CONTINUE; out: return (result); #else UNUSED(name); UNUSED(intoken); UNUSED(outtoken); UNUSED(gssctx); UNUSED(mctx); UNUSED(err_message); return (ISC_R_NOTIMPLEMENTED); #endif }
int /* O - 0 on success, -1 on error */ _cupsSetNegotiateAuthString( http_t *http, /* I - Connection to server */ const char *method, /* I - Request method ("GET", "POST", "PUT") */ const char *resource) /* I - Resource path */ { OM_uint32 minor_status, /* Minor status code */ major_status; /* Major status code */ gss_buffer_desc output_token = GSS_C_EMPTY_BUFFER; /* Output token */ (void)method; (void)resource; # ifdef __APPLE__ /* * If the weak-linked GSSAPI/Kerberos library is not present, don't try * to use it... */ if (&gss_init_sec_context == NULL) { DEBUG_puts("1_cupsSetNegotiateAuthString: Weak-linked GSSAPI/Kerberos " "framework is not present"); return (-1); } # endif /* __APPLE__ */ if (http->gssname == GSS_C_NO_NAME) { http->gssname = cups_gss_getname(http, _cupsGSSServiceName()); } if (http->gssctx != GSS_C_NO_CONTEXT) { gss_delete_sec_context(&minor_status, &http->gssctx, GSS_C_NO_BUFFER); http->gssctx = GSS_C_NO_CONTEXT; } major_status = gss_init_sec_context(&minor_status, GSS_C_NO_CREDENTIAL, &http->gssctx, http->gssname, http->gssmech, GSS_C_MUTUAL_FLAG | GSS_C_INTEG_FLAG, GSS_C_INDEFINITE, GSS_C_NO_CHANNEL_BINDINGS, GSS_C_NO_BUFFER, &http->gssmech, &output_token, NULL, NULL); #ifdef HAVE_GSS_ACQUIRE_CRED_EX_F if (major_status == GSS_S_NO_CRED) { /* * Ask the user for credentials... */ char prompt[1024], /* Prompt for user */ userbuf[256]; /* Kerberos username */ const char *username, /* Username string */ *password; /* Password string */ _cups_gss_acquire_t data; /* Callback data */ gss_auth_identity_desc identity; /* Kerberos user identity */ _cups_globals_t *cg = _cupsGlobals(); /* Per-thread global data */ if (!cg->lang_default) cg->lang_default = cupsLangDefault(); snprintf(prompt, sizeof(prompt), _cupsLangString(cg->lang_default, _("Password for %s on %s? ")), cupsUser(), http->gsshost); if ((password = cupsGetPassword2(prompt, http, method, resource)) == NULL) return (-1); /* * Try to acquire credentials... */ username = cupsUser(); if (!strchr(username, '@')) { snprintf(userbuf, sizeof(userbuf), "%s@%s", username, http->gsshost); username = userbuf; } identity.type = GSS_AUTH_IDENTITY_TYPE_1; identity.flags = 0; identity.username = (char *)username; identity.realm = (char *)""; identity.password = (char *)password; identity.credentialsRef = NULL; data.sem = dispatch_semaphore_create(0); data.major = 0; data.creds = NULL; if (data.sem) { major_status = gss_acquire_cred_ex_f(NULL, GSS_C_NO_NAME, 0, GSS_C_INDEFINITE, GSS_KRB5_MECHANISM, GSS_C_INITIATE, &identity, &data, cups_gss_acquire); if (major_status == GSS_S_COMPLETE) { dispatch_semaphore_wait(data.sem, DISPATCH_TIME_FOREVER); major_status = data.major; } dispatch_release(data.sem); if (major_status == GSS_S_COMPLETE) { OM_uint32 release_minor; /* Minor status from releasing creds */ major_status = gss_init_sec_context(&minor_status, data.creds, &http->gssctx, http->gssname, http->gssmech, GSS_C_MUTUAL_FLAG | GSS_C_INTEG_FLAG, GSS_C_INDEFINITE, GSS_C_NO_CHANNEL_BINDINGS, GSS_C_NO_BUFFER, &http->gssmech, &output_token, NULL, NULL); gss_release_cred(&release_minor, &data.creds); } } } #endif /* HAVE_GSS_ACQUIRED_CRED_EX_F */ if (GSS_ERROR(major_status)) { cups_gss_printf(major_status, minor_status, "_cupsSetNegotiateAuthString: Unable to initialize " "security context"); return (-1); } #ifdef DEBUG else if (major_status == GSS_S_CONTINUE_NEEDED) cups_gss_printf(major_status, minor_status, "_cupsSetNegotiateAuthString: Continuation needed!"); #endif /* DEBUG */ if (output_token.length > 0 && output_token.length <= 65536) { /* * Allocate the authorization string since Windows KDCs can have * arbitrarily large credentials... */ int authsize = 10 + /* "Negotiate " */ (int)output_token.length * 4 / 3 + 1 + 1; /* Base64 + nul */ httpSetAuthString(http, NULL, NULL); if ((http->authstring = malloc((size_t)authsize)) == NULL) { http->authstring = http->_authstring; authsize = sizeof(http->_authstring); } strlcpy(http->authstring, "Negotiate ", (size_t)authsize); httpEncode64_2(http->authstring + 10, authsize - 10, output_token.value, (int)output_token.length); gss_release_buffer(&minor_status, &output_token); } else { DEBUG_printf(("1_cupsSetNegotiateAuthString: Kerberos credentials too " "large - %d bytes!", (int)output_token.length)); gss_release_buffer(&minor_status, &output_token); return (-1); } return (0); }