int ssh_gssapi_check_mechanism(Gssctxt **ctx, gss_OID oid, const char *host) { gss_buffer_desc token = GSS_C_EMPTY_BUFFER; OM_uint32 major, minor; gss_OID_desc spnego_oid = {6, (void *)"\x2B\x06\x01\x05\x05\x02"}; /* RFC 4462 says we MUST NOT do SPNEGO */ if (oid->length == spnego_oid.length && (memcmp(oid->elements, spnego_oid.elements, oid->length) == 0)) return 0; /* false */ ssh_gssapi_build_ctx(ctx); ssh_gssapi_set_oid(*ctx, oid); major = ssh_gssapi_import_name(*ctx, host); if (!GSS_ERROR(major)) { major = ssh_gssapi_init_ctx(*ctx, 0, GSS_C_NO_BUFFER, &token, NULL); gss_release_buffer(&minor, &token); if ((*ctx)->context != GSS_C_NO_CONTEXT) gss_delete_sec_context(&minor, &(*ctx)->context, GSS_C_NO_BUFFER); } if (GSS_ERROR(major)) ssh_gssapi_delete_ctx(ctx); return (!GSS_ERROR(major)); }
int ssh_gssapi_check_mechanism(gss_OID oid, const char *host) { Gssctxt * ctx = NULL; gss_buffer_desc token; OM_uint32 major,minor; ssh_gssapi_build_ctx(&ctx); ssh_gssapi_set_oid(ctx,oid); ssh_gssapi_import_name(ctx, (char *) host); major=ssh_gssapi_init_ctx(ctx,0, GSS_C_NO_BUFFER, &token, NULL); gss_release_buffer(&minor,&token); ssh_gssapi_delete_ctx(&ctx); return(!GSS_ERROR(major)); }
void input_gssapi_errtok(int type, u_int32_t plen, void *ctxt) { OM_uint32 min_status; Authctxt *authctxt = ctxt; Gssctxt *gssctxt; gss_buffer_desc send_tok, recv_tok; if (authctxt == NULL) fatal("input_gssapi_response: no authentication context"); gssctxt = authctxt->methoddata; recv_tok.value=packet_get_string(&recv_tok.length); /* Stick it into GSSAPI and see what it says */ (void) ssh_gssapi_init_ctx(gssctxt, authctxt->host, options.gss_deleg_creds, &recv_tok, &send_tok); xfree(recv_tok.value); (void) gss_release_buffer(&min_status, &send_tok); debug("Server sent a GSS-API error token during GSS userauth -- %s", ssh_gssapi_last_error(gssctxt, NULL, NULL)); packet_check_eom(); /* We can't send a packet to the server */ /* The draft says that we should wait for the server to fail * before starting the next authentication. So, we clear the * state, but don't do anything else */ clear_auth_state(authctxt); return; }
void kexgss_client(Kex *kex) { gss_buffer_desc send_tok = GSS_C_EMPTY_BUFFER; gss_buffer_desc recv_tok, gssbuf, msg_tok, *token_ptr; Gssctxt *ctxt; OM_uint32 maj_status, min_status, ret_flags; u_int klen, kout, slen = 0, hashlen, strlen; DH *dh; BIGNUM *dh_server_pub = NULL; BIGNUM *shared_secret = NULL; BIGNUM *p = NULL; BIGNUM *g = NULL; u_char *kbuf, *hash; u_char *serverhostkey = NULL; u_char *empty = ""; char *msg; char *lang; int type = 0; int first = 1; int nbits = 0, min = DH_GRP_MIN, max = DH_GRP_MAX; /* Initialise our GSSAPI world */ ssh_gssapi_build_ctx(&ctxt); if (ssh_gssapi_id_kex(ctxt, kex->name, kex->kex_type) == GSS_C_NO_OID) fatal("Couldn't identify host exchange"); if (ssh_gssapi_import_name(ctxt, kex->gss_host)) fatal("Couldn't import hostname"); if (kex->gss_client && ssh_gssapi_client_identity(ctxt, kex->gss_client)) fatal("Couldn't acquire client credentials"); switch (kex->kex_type) { case KEX_GSS_GRP1_SHA1: dh = dh_new_group1(); break; case KEX_GSS_GRP14_SHA1: dh = dh_new_group14(); break; case KEX_GSS_GEX_SHA1: debug("Doing group exchange\n"); nbits = dh_estimate(kex->we_need * 8); packet_start(SSH2_MSG_KEXGSS_GROUPREQ); packet_put_int(min); packet_put_int(nbits); packet_put_int(max); packet_send(); packet_read_expect(SSH2_MSG_KEXGSS_GROUP); if ((p = BN_new()) == NULL) fatal("BN_new() failed"); packet_get_bignum2(p); if ((g = BN_new()) == NULL) fatal("BN_new() failed"); packet_get_bignum2(g); packet_check_eom(); if (BN_num_bits(p) < min || BN_num_bits(p) > max) fatal("GSSGRP_GEX group out of range: %d !< %d !< %d", min, BN_num_bits(p), max); dh = dh_new_group(g, p); break; default: fatal("%s: Unexpected KEX type %d", __func__, kex->kex_type); } /* Step 1 - e is dh->pub_key */ dh_gen_key(dh, kex->we_need * 8); /* This is f, we initialise it now to make life easier */ dh_server_pub = BN_new(); if (dh_server_pub == NULL) fatal("dh_server_pub == NULL"); token_ptr = GSS_C_NO_BUFFER; do { debug("Calling gss_init_sec_context"); maj_status = ssh_gssapi_init_ctx(ctxt, kex->gss_deleg_creds, token_ptr, &send_tok, &ret_flags); if (GSS_ERROR(maj_status)) { if (send_tok.length != 0) { packet_start(SSH2_MSG_KEXGSS_CONTINUE); packet_put_string(send_tok.value, send_tok.length); } fatal("gss_init_context failed"); } /* If we've got an old receive buffer get rid of it */ if (token_ptr != GSS_C_NO_BUFFER) xfree(recv_tok.value); if (maj_status == GSS_S_COMPLETE) { /* If mutual state flag is not true, kex fails */ if (!(ret_flags & GSS_C_MUTUAL_FLAG)) fatal("Mutual authentication failed"); /* If integ avail flag is not true kex fails */ if (!(ret_flags & GSS_C_INTEG_FLAG)) fatal("Integrity check failed"); } /* * If we have data to send, then the last message that we * received cannot have been a 'complete'. */ if (send_tok.length != 0) { if (first) { packet_start(SSH2_MSG_KEXGSS_INIT); packet_put_string(send_tok.value, send_tok.length); packet_put_bignum2(dh->pub_key); first = 0; } else { packet_start(SSH2_MSG_KEXGSS_CONTINUE); packet_put_string(send_tok.value, send_tok.length); } packet_send(); gss_release_buffer(&min_status, &send_tok); /* If we've sent them data, they should reply */ do { type = packet_read(); if (type == SSH2_MSG_KEXGSS_HOSTKEY) { debug("Received KEXGSS_HOSTKEY"); if (serverhostkey) fatal("Server host key received more than once"); serverhostkey = packet_get_string(&slen); } } while (type == SSH2_MSG_KEXGSS_HOSTKEY); switch (type) { case SSH2_MSG_KEXGSS_CONTINUE: debug("Received GSSAPI_CONTINUE"); if (maj_status == GSS_S_COMPLETE) fatal("GSSAPI Continue received from server when complete"); recv_tok.value = packet_get_string(&strlen); recv_tok.length = strlen; break; case SSH2_MSG_KEXGSS_COMPLETE: debug("Received GSSAPI_COMPLETE"); packet_get_bignum2(dh_server_pub); msg_tok.value = packet_get_string(&strlen); msg_tok.length = strlen; /* Is there a token included? */ if (packet_get_char()) { recv_tok.value= packet_get_string(&strlen); recv_tok.length = strlen; /* If we're already complete - protocol error */ if (maj_status == GSS_S_COMPLETE) packet_disconnect("Protocol error: received token when complete"); } else { /* No token included */ if (maj_status != GSS_S_COMPLETE) packet_disconnect("Protocol error: did not receive final token"); } break; case SSH2_MSG_KEXGSS_ERROR: debug("Received Error"); maj_status = packet_get_int(); min_status = packet_get_int(); msg = packet_get_string(NULL); lang = packet_get_string(NULL); fatal("GSSAPI Error: \n%.400s",msg); default: packet_disconnect("Protocol error: didn't expect packet type %d", type); } token_ptr = &recv_tok; } else { /* No data, and not complete */ if (maj_status != GSS_S_COMPLETE) fatal("Not complete, and no token output"); } } while (maj_status & GSS_S_CONTINUE_NEEDED); /* * We _must_ have received a COMPLETE message in reply from the * server, which will have set dh_server_pub and msg_tok */ if (type != SSH2_MSG_KEXGSS_COMPLETE) fatal("Didn't receive a SSH2_MSG_KEXGSS_COMPLETE when I expected it"); /* Check f in range [1, p-1] */ if (!dh_pub_is_valid(dh, dh_server_pub)) packet_disconnect("bad server public DH value"); /* compute K=f^x mod p */ klen = DH_size(dh); kbuf = xmalloc(klen); kout = DH_compute_key(kbuf, dh_server_pub, dh); if (kout < 0) fatal("DH_compute_key: failed"); shared_secret = BN_new(); if (shared_secret == NULL) fatal("kexgss_client: BN_new failed"); if (BN_bin2bn(kbuf, kout, shared_secret) == NULL) fatal("kexdh_client: BN_bin2bn failed"); memset(kbuf, 0, klen); xfree(kbuf); switch (kex->kex_type) { case KEX_GSS_GRP1_SHA1: case KEX_GSS_GRP14_SHA1: kex_dh_hash( kex->client_version_string, kex->server_version_string, buffer_ptr(&kex->my), buffer_len(&kex->my), buffer_ptr(&kex->peer), buffer_len(&kex->peer), (serverhostkey ? serverhostkey : empty), slen, dh->pub_key, /* e */ dh_server_pub, /* f */ shared_secret, /* K */ &hash, &hashlen ); break; case KEX_GSS_GEX_SHA1: kexgex_hash( kex->evp_md, kex->client_version_string, kex->server_version_string, buffer_ptr(&kex->my), buffer_len(&kex->my), buffer_ptr(&kex->peer), buffer_len(&kex->peer), (serverhostkey ? serverhostkey : empty), slen, min, nbits, max, dh->p, dh->g, dh->pub_key, dh_server_pub, shared_secret, &hash, &hashlen ); break; default: fatal("%s: Unexpected KEX type %d", __func__, kex->kex_type); } gssbuf.value = hash; gssbuf.length = hashlen; /* Verify that the hash matches the MIC we just got. */ if (GSS_ERROR(ssh_gssapi_checkmic(ctxt, &gssbuf, &msg_tok))) packet_disconnect("Hash's MIC didn't verify"); xfree(msg_tok.value); DH_free(dh); if (serverhostkey) xfree(serverhostkey); BN_clear_free(dh_server_pub); /* save session id */ if (kex->session_id == NULL) { kex->session_id_len = hashlen; kex->session_id = xmalloc(kex->session_id_len); memcpy(kex->session_id, hash, kex->session_id_len); } if (kex->gss_deleg_creds) ssh_gssapi_credentials_updated(ctxt); if (gss_kex_context == NULL) gss_kex_context = ctxt; else ssh_gssapi_delete_ctx(&ctxt); kex_derive_keys(kex, hash, hashlen, shared_secret); BN_clear_free(shared_secret); kex_finish(kex); }
void input_gssapi_token(int type, u_int32_t plen, void *ctxt) { Authctxt *authctxt = ctxt; Gssctxt *gssctxt; gss_buffer_desc send_tok, recv_tok, g_mic_data; Buffer mic_data; OM_uint32 status; u_int slen; if (authctxt == NULL || authctxt->method == NULL) fatal("input_gssapi_response: no authentication context"); gssctxt = authctxt->methoddata; recv_tok.value=packet_get_string(&slen); recv_tok.length=slen; /* safe typecast */ status=ssh_gssapi_init_ctx(gssctxt, authctxt->host, options.gss_deleg_creds, &recv_tok, &send_tok); packet_check_eom(); if (GSS_ERROR(status)) { if (send_tok.length>0) { packet_start(SSH2_MSG_USERAUTH_GSSAPI_ERRTOK); packet_put_string(send_tok.value,send_tok.length); packet_send(); packet_write_wait(); } /* Start again with the next method in the list */ clear_auth_state(authctxt); userauth(authctxt,NULL); return; } if (send_tok.length>0) { packet_start(SSH2_MSG_USERAUTH_GSSAPI_TOKEN); packet_put_string(send_tok.value,send_tok.length); packet_send(); packet_write_wait(); } if (status != GSS_S_COMPLETE) return; /* Make data buffer to MIC */ buffer_init(&mic_data); buffer_put_string(&mic_data, session_id2, session_id2_len); buffer_put_char(&mic_data, SSH2_MSG_USERAUTH_REQUEST); buffer_put_cstring(&mic_data, authctxt->server_user); buffer_put_cstring(&mic_data, authctxt->service); buffer_put_cstring(&mic_data, authctxt->method->name); /* Make MIC */ g_mic_data.value = buffer_ptr(&mic_data); g_mic_data.length = buffer_len(&mic_data); status = ssh_gssapi_get_mic(gssctxt, &g_mic_data, &send_tok); buffer_clear(&mic_data); if (GSS_ERROR(status) || send_tok.length == 0) { /* * Oops, now what? There's no error token... * Next userauth */ debug("GSS_GetMIC() failed! - " "Abandoning GSSAPI userauth"); clear_auth_state(authctxt); userauth(authctxt,NULL); return; } packet_start(SSH2_MSG_USERAUTH_GSSAPI_MIC); packet_put_string(send_tok.value,send_tok.length); packet_send(); packet_write_wait(); }
void input_gssapi_response(int type, u_int32_t plen, void *ctxt) { Authctxt *authctxt = ctxt; Gssctxt *gssctxt; OM_uint32 status,ms; u_int oidlen; char *oidv; gss_buffer_desc send_tok; if (authctxt == NULL) fatal("input_gssapi_response: no authentication context"); gssctxt = authctxt->methoddata; /* Setup our OID */ oidv=packet_get_string(&oidlen); if (datafellows & SSH_BUG_GSSAPI_BER) { if (!ssh_gssapi_check_mech_oid(gssctxt,oidv,oidlen)) { gss_OID oid; oid = ssh_gssapi_make_oid(oidlen, oidv); debug("Server returned different OID (%s) than expected (%s)", ssh_gssapi_oid_to_str(oid), ssh_gssapi_oid_to_str(gssctxt->desired_mech)); ssh_gssapi_release_oid(&oid); clear_auth_state(authctxt); userauth(authctxt,NULL); return; } } else { if(oidv[0]!=0x06 || oidv[1]!=oidlen-2) { debug("Badly encoded mechanism OID received"); clear_auth_state(authctxt); userauth(authctxt,NULL); return; } if (!ssh_gssapi_check_mech_oid(gssctxt,oidv+2,oidlen-2)) { gss_OID oid; oid = ssh_gssapi_make_oid(oidlen-2, oidv+2); debug("Server returned different OID (%s) than expected (%s)", ssh_gssapi_oid_to_str(oid), ssh_gssapi_oid_to_str(gssctxt->desired_mech)); clear_auth_state(authctxt); userauth(authctxt,NULL); return; } } packet_check_eom(); dispatch_set(SSH2_MSG_USERAUTH_GSSAPI_TOKEN,&input_gssapi_token); dispatch_set(SSH2_MSG_USERAUTH_GSSAPI_ERROR,&input_gssapi_error); dispatch_set(SSH2_MSG_USERAUTH_GSSAPI_ERRTOK,&input_gssapi_errtok); status = ssh_gssapi_init_ctx(gssctxt, authctxt->host, options.gss_deleg_creds, GSS_C_NO_BUFFER, &send_tok); if (GSS_ERROR(status)) { if (send_tok.length>0) { packet_start(SSH2_MSG_USERAUTH_GSSAPI_ERRTOK); packet_put_string(send_tok.value,send_tok.length); packet_send(); packet_write_wait(); } /* Start again with next method on list */ debug("Trying to start again"); clear_auth_state(authctxt); userauth(authctxt,NULL); return; } /* We must have data to send */ packet_start(SSH2_MSG_USERAUTH_GSSAPI_TOKEN); packet_put_string(send_tok.value,send_tok.length); packet_send(); packet_write_wait(); gss_release_buffer(&ms, &send_tok); }
void ssh_gssapi_client_mechs(const char *server_host, gss_OID_set *mechs) { gss_OID_set indicated = GSS_C_NULL_OID_SET; gss_OID_set acquired, supported; gss_OID mech; gss_cred_id_t creds; Gssctxt *ctxt = NULL; gss_buffer_desc tok; OM_uint32 maj, min; int i; char *errmsg; if (!mechs) return; *mechs = GSS_C_NULL_OID_SET; maj = gss_indicate_mechs(&min, &indicated); if (GSS_ERROR(maj)) { debug("No GSS-API mechanisms are installed"); return; } maj = gss_create_empty_oid_set(&min, &supported); if (GSS_ERROR(maj)) { errmsg = ssh_gssapi_last_error(NULL, &maj, &min); debug("Failed to allocate resources (%s) for GSS-API", errmsg); xfree(errmsg); (void) gss_release_oid_set(&min, &indicated); return; } maj = gss_acquire_cred(&min, GSS_C_NO_NAME, 0, indicated, GSS_C_INITIATE, &creds, &acquired, NULL); if (GSS_ERROR(maj)) { errmsg = ssh_gssapi_last_error(NULL, &maj, &min); debug("Failed to acquire GSS-API credentials for any " "mechanisms (%s)", errmsg); xfree(errmsg); (void) gss_release_oid_set(&min, &indicated); (void) gss_release_oid_set(&min, &supported); return; } (void) gss_release_cred(&min, &creds); for (i = 0; i < acquired->count; i++) { mech = &acquired->elements[i]; if (ssh_gssapi_is_spnego(mech)) continue; ssh_gssapi_build_ctx(&ctxt, 1, mech); if (!ctxt) continue; /* * This is useful for mechs like Kerberos, which can * detect unknown target princs here, but not for * mechs like SPKM, which cannot detect unknown princs * until context tokens are actually exchanged. * * 'Twould be useful to have a test that could save us * the bother of trying this for SPKM and the such... */ maj = ssh_gssapi_init_ctx(ctxt, server_host, 0, NULL, &tok); if (GSS_ERROR(maj)) { errmsg = ssh_gssapi_last_error(ctxt, NULL, NULL); debug("Skipping GSS-API mechanism %s (%s)", ssh_gssapi_oid_to_name(mech), errmsg); xfree(errmsg); continue; } (void) gss_release_buffer(&min, &tok); maj = gss_add_oid_set_member(&min, mech, &supported); if (GSS_ERROR(maj)) { errmsg = ssh_gssapi_last_error(NULL, &maj, &min); debug("Failed to allocate resources (%s) for GSS-API", errmsg); xfree(errmsg); } } *mechs = supported; }