int _gsasl_kerberos_v5_client_step (Gsasl_session * sctx, void *mech_data, const char *input, size_t input_len, char *output, size_t * output_len) { struct _Gsasl_kerberos_v5_client_state *state = mech_data; Gsasl_client_callback_authentication_id cb_authentication_id; Gsasl_client_callback_authorization_id cb_authorization_id; Gsasl_client_callback_qop cb_qop; Gsasl_client_callback_realm cb_realm; Gsasl_client_callback_password cb_password; Gsasl_client_callback_service cb_service; Gsasl_client_callback_maxbuf cb_maxbuf; Gsasl_ctx *ctx; int res; int len; ctx = gsasl_client_ctx_get (sctx); if (ctx == NULL) return GSASL_CANNOT_GET_CTX; /* These are optional */ cb_realm = gsasl_client_callback_realm_get (ctx); cb_service = gsasl_client_callback_service_get (ctx); cb_authentication_id = gsasl_client_callback_authentication_id_get (ctx); cb_authorization_id = gsasl_client_callback_authorization_id_get (ctx); cb_qop = gsasl_client_callback_qop_get (ctx); cb_maxbuf = gsasl_client_callback_maxbuf_get (ctx); /* Only optionally needed in infrastructure mode */ cb_password = gsasl_client_callback_password_get (ctx); if (cb_password == NULL) return GSASL_NEED_CLIENT_PASSWORD_CALLBACK; /* I think we really need this one */ cb_service = gsasl_client_callback_service_get (ctx); if (cb_service == NULL) return GSASL_NEED_CLIENT_SERVICE_CALLBACK; switch (state->step) { case STEP_FIRST: if (input == NULL) { *output_len = 0; return GSASL_NEEDS_MORE; } if (input_len != SERVER_HELLO_LEN) return GSASL_MECHANISM_PARSE_ERROR; memcpy (state->serverhello, input, input_len); { unsigned char serverbitmap; memcpy (&serverbitmap, input, BITMAP_LEN); state->serverqops = 0; if (serverbitmap & GSASL_QOP_AUTH) state->serverqops |= GSASL_QOP_AUTH; if (serverbitmap & GSASL_QOP_AUTH_INT) state->serverqops |= GSASL_QOP_AUTH_INT; if (serverbitmap & GSASL_QOP_AUTH_CONF) state->serverqops |= GSASL_QOP_AUTH_CONF; if (serverbitmap & MUTUAL) state->servermutual = 1; } memcpy (&state->servermaxbuf, &input[BITMAP_LEN], MAXBUF_LEN); state->servermaxbuf = ntohl (state->servermaxbuf); if (cb_qop) state->clientqop = cb_qop (sctx, state->serverqops); if (!(state->serverqops & state->clientqop & (GSASL_QOP_AUTH | GSASL_QOP_AUTH_INT | GSASL_QOP_AUTH_CONF))) return GSASL_AUTHENTICATION_ERROR; /* XXX for now we require server authentication */ if (!state->servermutual) return GSASL_AUTHENTICATION_ERROR; /* Decide policy here: non-infrastructure, infrastructure or proxy. * * A callback to decide should be added, but without the default * should be: * * IF shishi_tktset_get_for_server() THEN * INFRASTRUCTURE MODE * ELSE IF shishi_realm_for_server(server) THEN * PROXY INFRASTRUCTURE (then fallback to NIM?) * ELSE * NON-INFRASTRUCTURE MODE */ state->step = STEP_NONINFRA_SEND_APREQ; /* only NIM for now.. */ /* fall through */ case STEP_NONINFRA_SEND_ASREQ: res = shishi_as (state->sh, &state->as); if (res) return GSASL_KERBEROS_V5_INTERNAL_ERROR; if (cb_authentication_id) /* Shishi defaults to one otherwise */ { len = *output_len - 1; res = cb_authentication_id (sctx, output, &len); if (res != GSASL_OK) return res; output[len] = '\0'; res = shishi_kdcreq_set_cname (state->sh, shishi_as_req (state->as), SHISHI_NT_UNKNOWN, output); if (res != GSASL_OK) return res; } if (cb_realm) { len = *output_len - 1; res = cb_realm (sctx, output, &len); if (res != GSASL_OK) return res; } else len = 0; output[len] = '\0'; res = shishi_kdcreq_set_realm (state->sh, shishi_as_req (state->as), output); if (res != GSASL_OK) return res; if (cb_service) { char *sname[3]; size_t servicelen = 0; size_t hostnamelen = 0; res = cb_service (sctx, NULL, &servicelen, NULL, &hostnamelen, /* XXX support servicename a'la DIGEST-MD5 too? */ NULL, NULL); if (res != GSASL_OK) return res; if (*output_len < servicelen + 1 + hostnamelen + 1) return GSASL_TOO_SMALL_BUFFER; sname[0] = &output[0]; sname[1] = &output[servicelen + 2]; sname[2] = NULL; res = cb_service (sctx, sname[0], &servicelen, sname[1], &hostnamelen, NULL, NULL); if (res != GSASL_OK) return res; sname[0][servicelen] = '\0'; sname[1][hostnamelen] = '\0'; res = shishi_kdcreq_set_sname (state->sh, shishi_as_req (state->as), SHISHI_NT_UNKNOWN, sname); if (res != GSASL_OK) return res; } /* XXX query application for encryption types and set the etype field? Already configured by shishi though... */ res = shishi_a2d (state->sh, shishi_as_req (state->as), output, output_len); if (res != SHISHI_OK) return GSASL_KERBEROS_V5_INTERNAL_ERROR; state->step = STEP_NONINFRA_WAIT_ASREP; res = GSASL_NEEDS_MORE; break; case STEP_NONINFRA_WAIT_ASREP: if (shishi_as_rep_der_set (state->as, input, input_len) != SHISHI_OK) return GSASL_MECHANISM_PARSE_ERROR; /* XXX? password stored in callee's output buffer */ len = *output_len - 1; res = cb_password (sctx, output, &len); if (res != GSASL_OK && res != GSASL_NEEDS_MORE) return res; output[len] = '\0'; res = shishi_as_rep_process (state->as, NULL, output); if (res != SHISHI_OK) return GSASL_AUTHENTICATION_ERROR; state->step = STEP_NONINFRA_SEND_APREQ; /* fall through */ case STEP_NONINFRA_SEND_APREQ: if (*output_len <= CLIENT_HELLO_LEN + SERVER_HELLO_LEN) return GSASL_TOO_SMALL_BUFFER; if (!(state->clientqop & ~GSASL_QOP_AUTH)) state->clientmaxbuf = 0; else if (cb_maxbuf) state->clientmaxbuf = cb_maxbuf (sctx, state->servermaxbuf); else state->clientmaxbuf = MAXBUF_DEFAULT; /* XXX for now we require server authentication */ output[0] = state->clientqop | MUTUAL; { uint32_t tmp; tmp = ntohl (state->clientmaxbuf); memcpy (&output[BITMAP_LEN], &tmp, MAXBUF_LEN); } memcpy (&output[CLIENT_HELLO_LEN], state->serverhello, SERVER_HELLO_LEN); if (cb_authorization_id) { len = *output_len - CLIENT_HELLO_LEN + SERVER_HELLO_LEN; res = cb_authorization_id (sctx, &output[CLIENT_HELLO_LEN + SERVER_HELLO_LEN], &len); } else len = 0; len += CLIENT_HELLO_LEN + SERVER_HELLO_LEN; res = shishi_ap_tktoptionsdata (state->sh, &state->ap, shishi_as_tkt (state->as), SHISHI_APOPTIONS_MUTUAL_REQUIRED, output, len); if (res != SHISHI_OK) return GSASL_KERBEROS_V5_INTERNAL_ERROR; res = shishi_authenticator_add_authorizationdata (state->sh, shishi_ap_authenticator (state->ap), -1, output, len); if (res != SHISHI_OK) return GSASL_KERBEROS_V5_INTERNAL_ERROR; /* XXX set realm in AP-REQ and Authenticator */ res = shishi_ap_req_der (state->ap, output, output_len); if (res != SHISHI_OK) return GSASL_KERBEROS_V5_INTERNAL_ERROR; state->step = STEP_NONINFRA_WAIT_APREP; res = GSASL_NEEDS_MORE; break; case STEP_NONINFRA_WAIT_APREP: if (shishi_ap_rep_der_set (state->ap, input, input_len) != SHISHI_OK) return GSASL_MECHANISM_PARSE_ERROR; res = shishi_ap_rep_verify (state->ap); if (res != SHISHI_OK) return GSASL_AUTHENTICATION_ERROR; state->step = STEP_SUCCESS; /* XXX support AP session keys */ state->sessionkey = shishi_tkt_key (shishi_as_tkt (state->as)); *output_len = 0; res = GSASL_OK; break; default: res = GSASL_MECHANISM_CALLED_TOO_MANY_TIMES; break; } return res; }
/* shishi authentication */ int auth (Shishi * h, int verbose, const char *cname, const char *sname, int sock, char *cmd, char *port, Shishi_key ** enckey, Shishi_key * deckey) { Shishi_ap *ap; Shishi_tkt *tkt; Shishi_tkts_hint hint; int rc; char *out; int outlen; int krb5len, msglen; char auth; /* KERBEROS 5 SENDAUTH MESSAGE */ char krb5sendauth[] = "KRB5_SENDAUTH_V1.0"; /* PROTOCOL VERSION */ char krb5sendclient[] = "KCMDV0.2"; /* to store error msg sent by server */ char errormsg[101]; char cksumdata[101]; /* size of KRB5 auth message */ krb5len = strlen (krb5sendauth) + 1; msglen = htonl (krb5len); safewrite (sock, &msglen, sizeof (int)); /* KRB5 authentication message */ safewrite (sock, krb5sendauth, krb5len); /* size of client message */ krb5len = strlen (krb5sendclient) + 1; msglen = htonl (krb5len); safewrite (sock, &msglen, sizeof (int)); /* KRB5 client message */ safewrite (sock, krb5sendclient, krb5len); /* get answer from server 0 = ok, 1 = error with message */ read (sock, &auth, 1); if (auth) { read (sock, errormsg, 100); errormsg[100] = '\0'; printf ("Error during server authentication : %s\n", errormsg); return 1; } if (verbose) { printf ("Client: %s\n", cname); printf ("Server: %s\n", sname); } /* Get a ticket for the server. */ memset (&hint, 0, sizeof (hint)); hint.client = (char *) cname; hint.server = (char *) sname; tkt = shishi_tkts_get (shishi_tkts_default (h), &hint); if (!tkt) { printf ("cannot find ticket for \"%s\"\n", sname); return 1; } if (verbose) shishi_tkt_pretty_print (tkt, stderr); /* Create Authentication context */ rc = shishi_ap_tktoptions (h, &ap, tkt, SHISHI_APOPTIONS_MUTUAL_REQUIRED); if (rc != SHISHI_OK) { printf ("cannot create authentication context\n"); return 1; } /* checksum = port: terminal name */ snprintf (cksumdata, 100, "%s:%s%s", port, cmd, cname); /* add checksum to authenticator */ shishi_ap_authenticator_cksumdata_set (ap, cksumdata, strlen (cksumdata)); /* To be compatible with MIT rlogind */ shishi_ap_authenticator_cksumtype_set (ap, SHISHI_RSA_MD5); /* create der encoded AP-REQ */ rc = shishi_ap_req_der (ap, &out, &outlen); if (rc != SHISHI_OK) { printf ("cannot build authentication request: %s\n", shishi_strerror (rc)); return 1; } if (verbose) shishi_authenticator_print (h, stderr, shishi_ap_authenticator (ap)); /* extract subkey if present from ap exchange for secure connection */ shishi_authenticator_get_subkey (h, shishi_ap_authenticator (ap), enckey); /* send size of AP-REQ to the server */ msglen = htonl (outlen); safewrite (sock, (char *) &msglen, sizeof (int)); /* send AP-REQ to the server */ safewrite (sock, out, outlen); /* read a respond from server - what ? */ read (sock, &auth, sizeof (int)); /* For mutual authentication, wait for server reply. */ if (shishi_apreq_mutual_required_p (h, shishi_ap_req (ap))) { if (verbose) printf ("Waiting for server to authenticate itself...\n"); /* read size of the AP-REP */ read (sock, (char *) &outlen, sizeof (int)); /* read AP-REP */ outlen = ntohl (outlen); outlen = read (sock, out, outlen); rc = shishi_ap_rep_verify_der (ap, out, outlen); if (rc == SHISHI_OK) { if (verbose) printf ("AP-REP verification OK...\n"); } else { if (rc == SHISHI_APREP_VERIFY_FAILED) printf ("AP-REP verification failed...\n"); else printf ("AP-REP verification error: %s\n", shishi_strerror (rc)); return 1; } /* The server is authenticated. */ if (verbose) printf ("Server authenticated.\n"); } /* We are now authenticated. */ if (verbose) printf ("User authenticated.\n"); return AUTH_OK; }
/* Request part of gss_krb5_init_sec_context. Assumes that context_handle is valid, and has krb5 specific structure, and that output_token is valid and cleared. */ static OM_uint32 init_request (OM_uint32 * minor_status, const gss_cred_id_t initiator_cred_handle, 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) { gss_ctx_id_t ctx = *context_handle; _gss_krb5_ctx_t k5 = ctx->krb5; char *cksum, *der; size_t cksumlen; int rc; OM_uint32 maj_stat; gss_buffer_desc tmp; Shishi_tkts_hint hint; /* Get service ticket. */ maj_stat = gss_krb5_canonicalize_name (minor_status, target_name, GSS_C_NO_OID, &k5->peerptr); if (GSS_ERROR (maj_stat)) return maj_stat; memset (&hint, 0, sizeof (hint)); hint.server = k5->peerptr->value; hint.endtime = time_req; k5->tkt = shishi_tkts_get (shishi_tkts_default (k5->sh), &hint); if (!k5->tkt) { if (minor_status) *minor_status = GSS_KRB5_S_KG_CCACHE_NOMATCH; return GSS_S_NO_CRED; } /* Create Authenticator checksum field. */ maj_stat = _gss_krb5_checksum_pack (minor_status, initiator_cred_handle, context_handle, input_chan_bindings, req_flags, &cksum, &cksumlen); if (GSS_ERROR (maj_stat)) return maj_stat; /* Create AP-REQ in output_token. */ rc = shishi_ap_tktoptionsraw (k5->sh, &k5->ap, k5->tkt, SHISHI_APOPTIONS_MUTUAL_REQUIRED, 0x8003, cksum, cksumlen); free (cksum); if (rc != SHISHI_OK) return GSS_S_FAILURE; rc = shishi_authenticator_seqnumber_get (k5->sh, shishi_ap_authenticator (k5->ap), &k5->initseqnr); if (rc != SHISHI_OK) return GSS_S_FAILURE; rc = shishi_ap_req_der (k5->ap, &der, &tmp.length); if (rc != SHISHI_OK) return GSS_S_FAILURE; tmp.value = der; rc = gss_encapsulate_token_prefix (&tmp, TOK_AP_REQ, TOK_LEN, GSS_KRB5, output_token); free (der); if (!rc) return GSS_S_FAILURE; if (req_flags & GSS_C_MUTUAL_FLAG) return GSS_S_CONTINUE_NEEDED; return GSS_S_COMPLETE; }