static Shishi_asn1 _shishi_kdcreq (Shishi * handle, int as) { int res; Shishi_asn1 node; const char *servicebuf[3]; uint32_t nonce; if (as) node = shishi_asn1_asreq (handle); else node = shishi_asn1_tgsreq (handle); if (!node) return NULL; res = shishi_asn1_write (handle, node, "pvno", SHISHI_KDCREQ_DEFAULT_PVNO, SHISHI_KDCREQ_DEFAULT_PVNO_LEN); if (res != SHISHI_OK) goto error; if (as) res = shishi_asn1_write (handle, node, "msg-type", SHISHI_AS_REQ_DEFAULT_MSG_TYPE, SHISHI_AS_REQ_DEFAULT_MSG_TYPE_LEN); else res = shishi_asn1_write (handle, node, "msg-type", SHISHI_TGS_REQ_DEFAULT_MSG_TYPE, SHISHI_TGS_REQ_DEFAULT_MSG_TYPE_LEN); if (res != SHISHI_OK) goto error; res = shishi_asn1_write (handle, node, "req-body.kdc-options", SHISHI_KDCREQ_DEFAULT_REQ_BODY_KDC_OPTIONS, SHISHI_KDCREQ_DEFAULT_REQ_BODY_KDC_OPTIONS_LEN); if (res != SHISHI_OK) goto error; if (as) res = shishi_kdcreq_set_cname (handle, node, SHISHI_NT_PRINCIPAL, shishi_principal_default (handle)); else res = shishi_asn1_write (handle, node, "req-body.cname", NULL, 0); if (res != SHISHI_OK) goto error; res = shishi_kdcreq_set_realm (handle, node, shishi_realm_default (handle)); if (res != SHISHI_OK) goto error; servicebuf[0] = "krbtgt"; servicebuf[1] = shishi_realm_default (handle); servicebuf[2] = NULL; res = shishi_kdcreq_set_sname (handle, node, SHISHI_NT_PRINCIPAL, servicebuf); if (res != SHISHI_OK) goto error; res = shishi_asn1_write (handle, node, "req-body.sname.name-type", SHISHI_KDCREQ_DEFAULT_REQ_BODY_SNAME_NAME_TYPE, SHISHI_KDCREQ_DEFAULT_REQ_BODY_SNAME_NAME_TYPE_LEN); if (res != SHISHI_OK) goto error; res = shishi_asn1_write (handle, node, "req-body.till", shishi_generalize_time (handle, time (NULL) + handle->ticketlife), 0); if (res != SHISHI_OK) goto error; shishi_randomize (handle, 0, &nonce, sizeof (nonce)); nonce &= 0x7FFFFFFF; /* XXX fix _libtasn1_convert_integer. */ res = shishi_kdcreq_nonce_set (handle, node, nonce); if (res != SHISHI_OK) goto error; res = shishi_kdcreq_set_etype (handle, node, handle->clientkdcetypes, handle->nclientkdcetypes); if (res != SHISHI_OK) goto error; res = shishi_asn1_write (handle, node, "req-body.addresses", NULL, 0); if (res != SHISHI_OK) goto error; res = shishi_asn1_write (handle, node, "req-body.enc-authorization-data", NULL, 0); if (res != SHISHI_OK) goto error; res = shishi_asn1_write (handle, node, "req-body.additional-tickets", NULL, 0); if (res != SHISHI_OK) goto error; return node; error: shishi_asn1_done (handle, node); return NULL; }
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; }