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; }
void test (Shishi * handle) { Shishi_asn1 a; char *p, *buf, *buf2; int res; size_t n, m; int32_t t; uint32_t s; /* shishi_authenticator */ a = shishi_authenticator (handle); if (debug) printf ("shishi_authenticator () => `%p'.\n", a); if (a) success ("shishi_authenticator() OK\n"); else fail ("shishi_authenticator() failed\n"); if (debug) shishi_authenticator_print (handle, stdout, a); res = shishi_authenticator_remove_subkey (handle, a); if (res == SHISHI_OK) success ("shishi_authenticator_remove_subkey() OK\n"); else fail ("shishi_authenticator_remove_subkey() failed\n"); /* shishi_authenticator_seqnumber_get */ res = shishi_authenticator_seqnumber_get (handle, a, &s); if (res == SHISHI_OK) success ("shishi_authenticator_seqnumber_get() OK\n"); else fail ("shishi_authenticator_seqnumber_get() failed\n"); /* shishi_authenticator_seqnumber_set */ res = shishi_authenticator_seqnumber_set (handle, a, 42); if (res == SHISHI_OK) success ("shishi_authenticator_seqnumber_set() OK\n"); else fail ("shishi_authenticator_seqnumber_set() failed\n"); /* shishi_authenticator_seqnumber_get */ res = shishi_authenticator_seqnumber_get (handle, a, &s); if (res == SHISHI_OK && s == 42) success ("shishi_authenticator_seqnumber_get() OK\n"); else fail ("shishi_authenticator_seqnumber_get() failed\n"); /* shishi_authenticator_seqnumber_remove */ res = shishi_authenticator_seqnumber_remove (handle, a); if (res == SHISHI_OK) success ("shishi_authenticator_seqnumber_remove() OK\n"); else fail ("shishi_authenticator_seqnumber_remove() failed\n"); /* shishi_authenticator_set_crealm */ res = shishi_authenticator_set_crealm (handle, a, "foo"); if (res == SHISHI_OK) success ("shishi_authenticator_set_crealm() OK\n"); else fail ("shishi_authenticator_set_crealm() failed\n"); /* shishi_authenticator_client_set */ res = shishi_authenticator_client_set (handle, a, "foo/bar/baz"); if (res == SHISHI_OK) success ("shishi_authenticator_client_set() OK\n"); else fail ("shishi_authenticator_client_set() failed\n"); /* shishi_authenticator_client */ res = shishi_authenticator_client (handle, a, &buf, &n); if (debug) escapeprint (buf, n); if (res == SHISHI_OK && n == strlen ("foo/bar/baz") && memcmp (buf, "foo/bar/baz", n) == 0) success ("shishi_authenticator_client() OK\n"); else fail ("shishi_authenticator_client() failed\n"); if (res == SHISHI_OK) free (buf); /* shishi_authenticator_client_set */ res = shishi_authenticator_client_set (handle, a, "foo"); if (res == SHISHI_OK) success ("shishi_authenticator_client_set() OK\n"); else fail ("shishi_authenticator_client_set() failed\n"); /* shishi_authenticator_client */ res = shishi_authenticator_client (handle, a, &buf, &n); if (debug) escapeprint (buf, n); if (res == SHISHI_OK && n == strlen ("foo") && memcmp (buf, "foo", n) == 0) success ("shishi_authenticator_client() OK\n"); else fail ("shishi_authenticator_client() failed\n"); if (res == SHISHI_OK) free (buf); /* shishi_authenticator_set_crealm */ res = shishi_authenticator_set_crealm (handle, a, "bar"); if (res == SHISHI_OK) success ("shishi_authenticator_set_crealm() OK\n"); else fail ("shishi_authenticator_set_crealm() failed\n"); /* shishi_authenticator_clientrealm */ res = shishi_authenticator_clientrealm (handle, a, &buf, &n); if (debug) escapeprint (buf, n); if (res == SHISHI_OK && n == strlen ("foo@bar") && memcmp (buf, "foo@bar", n) == 0) success ("shishi_authenticator_clientrealm() OK\n"); else fail ("shishi_authenticator_clientrealm() failed\n"); if (res == SHISHI_OK) free (buf); /* shishi_authenticator_add_authorizationdata */ res = shishi_authenticator_add_authorizationdata (handle, a, 42, "baz", 3); if (res == SHISHI_OK) success ("shishi_authenticator_add_authorizationdata() OK\n"); else fail ("shishi_authenticator_add_authorizationdata() failed\n"); /* shishi_authenticator_authorizationdata */ res = shishi_authenticator_authorizationdata (handle, a, &t, &buf, &m, 1); if (debug) escapeprint (buf, m); if (res == SHISHI_OK && t == 42 && m == 3 && memcmp (buf, "baz", 3) == 0) success ("shishi_authenticator_authorizationdata() OK\n"); else fail ("shishi_authenticator_authorizationdata() failed\n"); if (res == SHISHI_OK) free (buf); /* shishi_authenticator_authorizationdata */ res = shishi_authenticator_authorizationdata (handle, a, &t, &buf, &m, 2); if (res == SHISHI_OK) free (buf); if (res == SHISHI_OUT_OF_RANGE) success ("shishi_authenticator_authorizationdata() OK\n"); else fail ("shishi_authenticator_authorizationdata() failed\n"); /* shishi_authenticator_remove_cksum */ res = shishi_authenticator_remove_cksum (handle, a); if (res == SHISHI_OK) success ("shishi_authenticator_remove_cksum() OK\n"); else fail ("shishi_authenticator_remove_cksum() failed\n"); /* shishi_asn1_to_der */ res = shishi_asn1_to_der (handle, a, &buf, &n); if (res == SHISHI_OK) success ("shishi_asn1_to_der() OK\n"); else n = 0, fail ("shishi_asn1_to_der() failed\n"); /* shishi_authenticator_to_file */ res = shishi_authenticator_to_file (handle, a, SHISHI_FILETYPE_TEXT, "authenticator.tmp"); if (res == SHISHI_OK) success ("shishi_authenticator_to_file() OK\n"); else fail ("shishi_authenticator_to_file() failed\n"); /* shishi_asn1_done */ shishi_asn1_done (handle, a); success ("shishi_asn1_done() OK\n"); a = NULL; /* shishi_authenticator_from_file */ res = shishi_authenticator_from_file (handle, &a, SHISHI_FILETYPE_TEXT, "authenticator.tmp"); if (res == SHISHI_OK) success ("shishi_authenticator_from_file() OK\n"); else fail ("shishi_authenticator_from_file() failed\n"); if (debug) { /* shishi_authenticator_print */ res = shishi_authenticator_print (handle, stdout, a); if (res == SHISHI_OK) success ("shishi_authenticator_print() OK\n"); else fail ("shishi_authenticator_print() failed\n"); } /* shishi_asn1_to_der */ res = shishi_asn1_to_der (handle, a, &buf2, &m); if (res == SHISHI_OK) success ("shishi_asn1_to_der() OK\n"); else n = 0, fail ("shishi_asn1_to_der() failed\n"); /* Compare DER encodings of authenticators */ if (n > 0 && m > 0 && n == m && memcmp (buf, buf2, n) == 0) success ("DER comparison OK\n"); else fail ("DER comparison failed\n"); free (buf); free (buf2); /* shishi_authenticator_cusec_set */ res = shishi_authenticator_cusec_set (handle, a, 4711); if (res == SHISHI_OK) success ("shishi_authenticator_cusec_set() OK\n"); else fail ("shishi_authenticator_cusec_set() failed\n"); /* shishi_authenticator_cusec_get */ res = shishi_authenticator_cusec_get (handle, a, &s); if (debug) printf ("shishi_authenticator_cusec_get () => `%d'.\n", t); if (res == SHISHI_OK && s == 4711) success ("shishi_authenticator_cusec_get() OK\n"); else fail ("shishi_authenticator_cusec_get() failed\n"); /* shishi_authenticator_ctime_set */ res = shishi_authenticator_ctime_set (handle, a, "19700101011831Z"); if (res == SHISHI_OK) success ("shishi_authenticator_ctime_set() OK\n"); else fail ("shishi_authenticator_ctime_set() failed\n"); /* shishi_authenticator_ctime */ res = shishi_authenticator_ctime (handle, a, &p); if (debug) escapeprint (p, strlen (p)); if (res == SHISHI_OK && memcmp (p, "19700101011831Z", 15) == 0) success ("shishi_authenticator_ctime() OK\n"); else fail ("shishi_authenticator_ctime() failed\n"); if (res == SHISHI_OK) free (p); /* shishi_asn1_to_der */ res = shishi_asn1_to_der (handle, a, &buf, &n); if (res == SHISHI_OK) success ("shishi_asn1_to_der() OK\n"); else n = 0, fail ("shishi_asn1_to_der() failed\n"); if (debug) { shishi_authenticator_print (handle, stdout, a); hexprint (buf, n); puts (""); hexprint (authenticator, sizeof (authenticator)); puts (""); } if (n == sizeof (authenticator) && n == AUTHENTICATOR_LEN && memcmp (authenticator, buf, n) == 0) success ("DER comparison OK\n"); else fail ("DER comparison failed\n"); free (buf); /* shishi_authenticator_clear_authorizationdata */ res = shishi_authenticator_clear_authorizationdata (handle, a); if (res == SHISHI_OK) success ("shishi_authenticator_clear_authorizationdata() OK\n"); else fail ("shishi_authenticator_clear_authorizationdata() failed\n"); /* shishi_asn1_to_der */ res = shishi_asn1_to_der (handle, a, &buf, &n); if (res == SHISHI_OK) success ("shishi_asn1_to_der() OK\n"); else n = 0, fail ("shishi_asn1_to_der() failed\n"); if (debug) { shishi_authenticator_print (handle, stdout, a); hexprint (buf, n); puts (""); hexprint (authenticator2, sizeof (authenticator2)); puts (""); } if (n == sizeof (authenticator2) && n == AUTHENTICATOR2_LEN && memcmp (authenticator2, buf, n) == 0) success ("DER comparison OK\n"); else fail ("DER comparison failed\n"); free (buf); /* unlink */ res = unlink ("authenticator.tmp"); if (res == 0) success ("unlink() OK\n"); else fail ("unlink() failed\n"); /* shishi_asn1_done */ shishi_asn1_done (handle, a); success ("shishi_asn1_done() OK\n"); }