diff options
Diffstat (limited to 'tests/lib/webkdc-login-t.c')
-rw-r--r-- | tests/lib/webkdc-login-t.c | 593 |
1 files changed, 593 insertions, 0 deletions
diff --git a/tests/lib/webkdc-login-t.c b/tests/lib/webkdc-login-t.c new file mode 100644 index 00000000..b82d5bdf --- /dev/null +++ b/tests/lib/webkdc-login-t.c @@ -0,0 +1,593 @@ +/* + * Test WebKDC login support. + * + * Written by Russ Allbery <rra@stanford.edu> + * Copyright 2011 + * The Board of Trustees of the Leland Stanford Junior University + * + * See LICENSE for licensing terms. + */ + +#include <config.h> +#include <portable/system.h> + +#include <apr_pools.h> +#include <apr_strings.h> +#include <apr_tables.h> +#include <time.h> + +#include <tests/tap/basic.h> +#include <tests/tap/kerberos.h> +#include <tests/tap/remctl.h> +#include <util/concat.h> +#include <webauth/basic.h> +#include <webauth/keys.h> +#include <webauth/tokens.h> +#include <webauth/webkdc.h> + + +int +main(void) +{ + apr_pool_t *pool = NULL; + WEBAUTH_KEYRING *ring, *session; + WEBAUTH_KEY *session_key; + char key_data[WA_AES_128], username[BUFSIZ], password[BUFSIZ]; + int status; + char *realm, *path, *keyring, *conf; + time_t now; + pid_t remctld; + FILE *file; + struct webauth_context *ctx; + struct webauth_webkdc_config config; + struct webauth_user_config user_config; + struct webauth_webkdc_login_request request; + struct webauth_webkdc_login_response *response; + struct webauth_token *token, login, wkproxy, wkproxy2; + struct webauth_token_request req; + struct webauth_token_webkdc_proxy *pt; + struct webauth_token_webkdc_service service; + struct webauth_webkdc_proxy_data *pd; + time_t expiration = 0; + + if (apr_initialize() != APR_SUCCESS) + bail("cannot initialize APR"); + if (apr_pool_create(&pool, NULL) != APR_SUCCESS) + bail("cannot create memory pool"); + if (webauth_context_init_apr(&ctx, pool) != WA_ERR_NONE) + bail("cannot initialize WebAuth context"); + + /* Load the precreated keyring that we'll use for token encryption. */ + keyring = test_file_path("data/keyring"); + status = webauth_keyring_read_file(keyring, &ring); + if (status != WA_ERR_NONE) + bail("cannot read %s: %s", keyring, + webauth_error_message(NULL, status)); + test_file_path_free(keyring); + + /* Ensure we have a basic configuration available. */ + memset(&config, 0, sizeof(config)); + config.local_realms = apr_array_make(pool, 1, sizeof(const char *)); + config.permitted_realms = apr_array_make(pool, 1, sizeof(const char *)); + memset(&user_config, 0, sizeof(user_config)); + if (chdir(getenv("SOURCE")) < 0) + bail("can't chdir to SOURCE"); + config.principal = kerberos_setup(); + if (config.principal == NULL) + skip_all("Kerberos tests not configured"); + realm = strchr(config.principal, '@'); + if (realm == NULL) + skip_all("Kerberos principal has no realm"); + realm++; + config.keytab_path = test_file_path("data/test.keytab"); + if (config.keytab_path == NULL) + skip_all("Kerberos tests not configured"); + + /* + * Ensure we have a username and password. + * + * FIXME: Ideally, we would skip the tests that don't require this and + * still do the rest. + */ + path = test_file_path("data/test.password"); + if (path == NULL) + skip_all("Kerberos tests not configured"); + file = fopen(path, "r"); + if (file == NULL) + sysbail("cannot open %s", path); + if (fgets(username, sizeof(username), file) == NULL) + bail("cannot read %s", path); + if (fgets(password, sizeof(password), file) == NULL) + bail("cannot read password from %s", path); + fclose(file); + if (username[strlen(username) - 1] != '\n') + bail("no newline in %s", path); + username[strlen(username) - 1] = '\0'; + if (password[strlen(password) - 1] != '\n') + bail("username or password too long in %s", path); + password[strlen(password) - 1] = '\0'; + test_file_path_free(path); + + /* + * FIXME: Similarly, if we don't have remctl, we should skip tests instead + * of skipping the whole thing. + */ +#ifndef PATH_REMCTLD + skip_all("remctld not found"); +#endif + if (chdir(getenv("SOURCE")) < 0) + bail("can't chdir to SOURCE"); + conf = concatpath(getenv("SOURCE"), "data/conf-webkdc"); + remctld = remctld_start(PATH_REMCTLD, config.principal, conf, NULL); + + plan(158); + + /* Provide basic configuration to the WebKDC code. */ + status = webauth_webkdc_config(ctx, &config); + is_int(WA_ERR_NONE, status, "WebKDC configuration succeeded"); + + /* Flesh out the absolute minimum required in the request. */ + now = time(NULL); + memset(&request, 0, sizeof(request)); + memset(&service, 0, sizeof(service)); + service.subject = "krb5:webauth/example.com@EXAMPLE.COM"; + if (webauth_random_key(key_data, sizeof(key_data)) != WA_ERR_NONE) + bail("cannot create random key"); + session_key = webauth_key_create(WA_AES_KEY, key_data, sizeof(key_data)); + status = webauth_keyring_from_key(ctx, session_key, &session); + if (status != WA_ERR_NONE) + bail("cannot create keyring from session key"); + service.session_key = session_key->data; + service.session_key_len = session_key->length; + service.creation = now; + service.expiration = now + 60; + request.service = &service; + memset(&req, 0, sizeof(req)); + req.type = "id"; + req.auth = "webkdc"; + req.return_url = "https://example.com/"; + req.creation = now; + request.request = &req; + request.creds = apr_array_make(pool, 1, sizeof(struct token *)); + + /* + * Attempted login with now proxy or login tokens. Should return an error + * indicating that a proxy token is required. + */ + status = webauth_webkdc_login(ctx, &request, &response, ring); + is_int(WA_ERR_NONE, status, "Minimal login returns success"); + is_int(WA_PEC_PROXY_TOKEN_REQUIRED, response->login_error, + "...with correct error code"); + is_string("need a proxy token", response->login_message, + "...and correct error message"); + ok(response->factors_wanted == NULL, "...no factors wanted"); + ok(response->factors_configured == NULL, "...no factors configured"); + ok(response->proxies == NULL, "...no new webkdc-proxy tokens"); + is_string("https://example.com/", response->return_url, + "...return URL is correct"); + is_string("krb5:webauth/example.com@EXAMPLE.COM", response->requester, + "...requester is correct"); + is_string(NULL, response->subject, "...no subject"); + is_string(NULL, response->result, "...no result token"); + is_string(NULL, response->result_type, "...no result type"); + is_string(NULL, response->login_cancel, "...no login cancel token"); + ok(response->app_state == NULL, "...no app state"); + is_int(0, response->app_state_len, "...no app state length"); + ok(response->logins == NULL, "...no login information"); + + /* Try again, but with a login cancel token requested. */ + req.options = "lc"; + status = webauth_webkdc_login(ctx, &request, &response, ring); + is_int(WA_ERR_NONE, status, "Login w/cancel returns success"); + is_int(WA_PEC_PROXY_TOKEN_REQUIRED, response->login_error, + "...with correct error code"); + is_string("need a proxy token", response->login_message, + "...and correct error message"); + ok(response->login_cancel != NULL, "...and now a cancel token"); + status = webauth_token_decode(ctx, WA_TOKEN_ERROR, response->login_cancel, + session, &token); + is_int(WA_ERR_NONE, status, "...which decodes properly"); + if (status != WA_ERR_NONE) + ok_block(3, 0, "...invalid error token"); + else { + is_int(WA_PEC_LOGIN_CANCELED, token->token.error.code, + "...with correct code"); + is_string("user canceled login", token->token.error.message, + "...and message"); + ok(token->token.error.creation - now < 3, "...and creation time"); + } + + /* Now add a login token and see if we can get an id token in response. */ + config.local_realms = apr_array_make(pool, 1, sizeof(const char *)); + APR_ARRAY_PUSH(config.local_realms, const char *) = "none"; + memset(&login, 0, sizeof(login)); + login.type = WA_TOKEN_LOGIN; + login.token.login.username = username; + login.token.login.password = password; + login.token.login.creation = now; + APR_ARRAY_PUSH(request.creds, struct webauth_token *) = &login; + status = webauth_webkdc_login(ctx, &request, &response, ring); + is_int(WA_ERR_NONE, status, "Login w/password returns success"); + is_int(0, response->login_error, "...with no error"); + is_string(NULL, response->login_message, "...and no message"); + ok(response->proxies != NULL, "...and now we have proxy tokens"); + pt = NULL; + if (response->proxies == NULL) + ok_block(11, 0, "...no proxy tokens"); + else { + is_int(1, response->proxies->nelts, "...one proxy token"); + pd = &APR_ARRAY_IDX(response->proxies, 0, + struct webauth_webkdc_proxy_data); + is_string("krb5", pd->type, "...of type krb5"); + status = webauth_token_decode(ctx, WA_TOKEN_WEBKDC_PROXY, pd->token, + ring, &token); + is_int(WA_ERR_NONE, status, "...which decodes properly"); + pt = &token->token.webkdc_proxy; + is_string(username, pt->subject, "...with correct subject"); + is_string("krb5", pt->proxy_type, "...and correct type"); + ok(strncmp("WEBKDC:krb5:", pt->proxy_subject, 12) == 0, + "...and correct proxy subject prefix"); + ok(strcmp(config.principal, pt->proxy_subject + 12) == 0, + "...and correct proxy subject identity"); + ok(pt->data != NULL, "...and data is not NULL"); + is_string("p", pt->initial_factors, "...and factors is password"); + ok(pt->creation - now < 3, "...and creation is okay"); + ok(pt->expiration > now, "...and expiration is sane"); + } + is_string(username, response->subject, "...subject is correct"); + ok(response->result != NULL, "...there is a result token"); + is_string("id", response->result_type, "...which is an id token"); + status = webauth_token_decode(ctx, WA_TOKEN_ID, response->result, + session, &token); + is_int(WA_ERR_NONE, status, "...result token decodes properly"); + if (status != WA_ERR_NONE) + ok_block(7, 0, "...no result token: %s", + webauth_error_message(ctx, status)); + else { + is_string(username, token->token.id.subject, + "...result subject is right"); + is_string("webkdc", token->token.id.auth, + "...result auth type is right"); + ok(token->token.id.auth_data == NULL, "...and there is no auth data"); + is_string("p", token->token.id.initial_factors, + "...result initial factors is right"); + is_int(0, token->token.id.loa, "...and no LoA"); + ok(token->token.id.creation - now < 3, "...and creation is sane"); + is_int(pt->expiration, token->token.id.expiration, + "...and expiration matches the expiration of the proxy token"); + } + + /* Get an id token with a Kerberos authenticator and test forced auth. */ + req.options = "lc,fa"; + request.creds = apr_array_make(pool, 2, sizeof(struct webauth_token *)); + APR_ARRAY_PUSH(request.creds, struct webauth_token *) = &login; + service.subject = apr_pstrcat(pool, "krb5:", config.principal, NULL); + req.auth = "krb5"; + status = webauth_webkdc_login(ctx, &request, &response, ring); + is_int(WA_ERR_NONE, status, "Login for krb5 auth returns success"); + is_int(0, response->login_error, "...with no error"); + is_string(NULL, response->login_message, "...and no message"); + ok(response->login_cancel != NULL, "...and there is a cancel token"); + ok(response->result != NULL, "...there is a result token"); + is_string("id", response->result_type, "...which is an id token"); + status = webauth_token_decode(ctx, WA_TOKEN_ID, response->result, + session, &token); + is_int(WA_ERR_NONE, status, "...result token decodes properly"); + if (status != WA_ERR_NONE) + ok_block(3, 0, "...no result token: %s", + webauth_error_message(ctx, status)); + else { + is_string(username, token->token.id.subject, + "...result subject is right"); + is_string("krb5", token->token.id.auth, + "...result auth type is right"); + ok(token->token.id.auth_data != NULL, "...and there is auth data"); + } + + /* Get a proxy token instead. */ + req.options = NULL; + request.creds = apr_array_make(pool, 2, sizeof(struct webauth_token *)); + APR_ARRAY_PUSH(request.creds, struct webauth_token *) = &login; + req.type = "proxy"; + req.auth = NULL; + req.proxy_type = "krb5"; + status = webauth_webkdc_login(ctx, &request, &response, ring); + is_int(WA_ERR_NONE, status, "Login for proxy token returns success"); + is_int(0, response->login_error, "...with no error"); + is_string(NULL, response->login_message, "...and no message"); + ok(response->login_cancel == NULL, "...and there is no cancel token"); + is_string(username, response->subject, "...subject is correct"); + pt = NULL; + if (response->proxies == NULL) + ok_block(3, 0, "...no proxy tokens"); + else { + is_int(1, response->proxies->nelts, "...one proxy token"); + pd = &APR_ARRAY_IDX(response->proxies, 0, + struct webauth_webkdc_proxy_data); + is_string("krb5", pd->type, "...of type krb5"); + status = webauth_token_decode(ctx, WA_TOKEN_WEBKDC_PROXY, pd->token, + ring, &token); + is_int(WA_ERR_NONE, status, "...which decodes properly"); + expiration = token->token.webkdc_proxy.expiration; + } + ok(response->result != NULL, "...there is a result token"); + is_string("proxy", response->result_type, "...which is a proxy token"); + status = webauth_token_decode(ctx, WA_TOKEN_PROXY, response->result, + session, &token); + is_int(WA_ERR_NONE, status, "...result token decodes properly"); + if (status != WA_ERR_NONE) + ok_block(6, 0, "...no result token: %s", + webauth_error_message(ctx, status)); + else { + is_string(username, token->token.proxy.subject, + "...result subject is right"); + is_string("krb5", token->token.proxy.type, + "...result proxy type is right"); + is_string("p", token->token.proxy.initial_factors, + "...result initial factors is right"); + is_int(0, token->token.proxy.loa, "...and no LoA"); + ok(token->token.proxy.creation - now < 3, "...and creation is sane"); + is_int(expiration, token->token.proxy.expiration, + "...and expiration matches the expiration of the proxy token"); + status = webauth_token_decode_raw(ctx, WA_TOKEN_WEBKDC_PROXY, + token->token.proxy.webkdc_proxy, + token->token.proxy.webkdc_proxy_len, + ring, &token); + is_int(WA_ERR_NONE, status, "...embedded webkdc-proxy token decodes"); + if (status != WA_ERR_NONE) + ok_block(7, 0, "...no webkdc-proxy token: %s", + webauth_error_message(ctx, status)); + else { + pt = &token->token.webkdc_proxy; + is_string(username, pt->subject, "...with correct subject"); + is_string("krb5", pt->proxy_type, "...and correct type"); + ok(strcmp(request.service->subject, pt->proxy_subject) == 0, + "...and correct proxy subject identity"); + ok(pt->data != NULL, "...and data is not NULL"); + is_string("p", pt->initial_factors, "...and factors is password"); + ok(pt->creation - now < 3, "...and creation is okay"); + ok(pt->expiration > now, "...and expiration is sane"); + } + } + + /* Get an id token with a single sign-on webkdc-proxy token. */ + memset(&wkproxy, 0, sizeof(wkproxy)); + wkproxy.type = WA_TOKEN_WEBKDC_PROXY; + wkproxy.token.webkdc_proxy.subject = "testuser"; + wkproxy.token.webkdc_proxy.proxy_type = "remuser"; + wkproxy.token.webkdc_proxy.proxy_subject = "WEBKDC:remuser"; + wkproxy.token.webkdc_proxy.data = "testuser"; + wkproxy.token.webkdc_proxy.data_len = strlen("testuser"); + wkproxy.token.webkdc_proxy.initial_factors = "x,x1"; + wkproxy.token.webkdc_proxy.loa = 3; + wkproxy.token.webkdc_proxy.creation = now; + wkproxy.token.webkdc_proxy.expiration = now + 60 * 60; + request.creds = apr_array_make(pool, 2, sizeof(struct webauth_token *)); + APR_ARRAY_PUSH(request.creds, struct webauth_token *) = &wkproxy; + req.type = "id"; + req.auth = "webkdc"; + req.proxy_type = NULL; + status = webauth_webkdc_login(ctx, &request, &response, ring); + if (status != WA_ERR_NONE) + diag("error status: %s", webauth_error_message(ctx, status)); + is_int(WA_ERR_NONE, status, "Proxy auth for webkdc returns success"); + is_int(0, response->login_error, "...with no error"); + is_string(NULL, response->login_message, "...and no message"); + ok(response->proxies != NULL, "...and we have proxy tokens"); + if (response->proxies == NULL) + ok_block(5, 0, "...no proxy tokens"); + else { + is_int(1, response->proxies->nelts, "...one proxy token"); + pd = &APR_ARRAY_IDX(response->proxies, 0, + struct webauth_webkdc_proxy_data); + is_string("remuser", pd->type, "...of type webkdc"); + status = webauth_token_decode(ctx, WA_TOKEN_WEBKDC_PROXY, pd->token, + ring, &token); + is_int(WA_ERR_NONE, status, "...which decodes properly"); + pt = &token->token.webkdc_proxy; + is_string("testuser", pt->subject, "...with correct subject"); + is_string("x,x1", pt->initial_factors, "...and initial factors"); + } + ok(response->result != NULL, "...there is a result token"); + is_string("id", response->result_type, "...which is an id token"); + status = webauth_token_decode(ctx, WA_TOKEN_ID, response->result, + session, &token); + is_int(WA_ERR_NONE, status, "...result token decodes properly"); + if (status != WA_ERR_NONE) + ok_block(6, 0, "...no result token: %s", + webauth_error_message(ctx, status)); + else { + is_string("testuser", token->token.id.subject, + "...result subject is right"); + is_string("webkdc", token->token.id.auth, + "...result auth type is right"); + is_string("x,x1", token->token.proxy.initial_factors, + "...result initial factors is right"); + is_int(3, token->token.id.loa, "...result LoA is right"); + ok(token->token.id.creation - now < 3, "...and creation is sane"); + is_int(now + 60 * 60, token->token.id.expiration, + "...and expiration matches the expiration of the proxy token"); + } + + /* Set forced authentication and try again. */ + req.options = "fa"; + status = webauth_webkdc_login(ctx, &request, &response, ring); + is_int(WA_ERR_NONE, status, "Proxy auth w/forced login returns success"); + is_int(WA_PEC_LOGIN_FORCED, response->login_error, + "...with the right error"); + is_string("forced authentication, need to login", response->login_message, + "...and the right message"); + is_string("testuser", response->subject, "...but we do know the subject"); + + /* Remove forced authentication but ask for a proxy token. */ + req.options = NULL; + req.type = "proxy"; + req.auth = NULL; + req.proxy_type = "krb5"; + status = webauth_webkdc_login(ctx, &request, &response, ring); + is_int(WA_ERR_NONE, status, "Proxy auth for proxy returns success"); + is_int(WA_PEC_PROXY_TOKEN_REQUIRED, response->login_error, + "...with the right error"); + is_string("need a proxy token", response->login_message, + "...and the right message"); + is_string("testuser", response->subject, "...but we do know the subject"); + + /* Now, add configuration for user information, and try this again. */ + user_config.protocol = WA_PROTOCOL_REMCTL; + user_config.host = "localhost"; + user_config.port = 14373; + user_config.identity = config.principal; + user_config.command = "test"; + status = webauth_user_config(ctx, &user_config); + is_int(WA_ERR_NONE, status, "User information config accepted"); + req.type = "id"; + req.auth = "webkdc"; + req.proxy_type = NULL; + wkproxy.token.webkdc_proxy.subject = "mini"; + wkproxy.token.webkdc_proxy.data = "mini"; + wkproxy.token.webkdc_proxy.data_len = strlen("mini"); + status = webauth_webkdc_login(ctx, &request, &response, ring); + if (status != WA_ERR_NONE) + diag("error status: %s", webauth_error_message(ctx, status)); + is_int(WA_ERR_NONE, status, "Proxy auth w/user config returns success"); + is_int(0, response->login_error, "...with no error"); + is_string(NULL, response->login_message, "...and no message"); + ok(response->result != NULL, "...there is a result token"); + is_string("id", response->result_type, "...which is an id token"); + status = webauth_token_decode(ctx, WA_TOKEN_ID, response->result, + session, &token); + is_int(WA_ERR_NONE, status, "...result token decodes properly"); + if (status != WA_ERR_NONE) + ok_block(4, 0, "...no result token: %s", + webauth_error_message(ctx, status)); + else { + is_string("mini", token->token.id.subject, + "...result subject is right"); + is_string("webkdc", token->token.id.auth, + "...result auth type is right"); + is_string("x,x1", token->token.proxy.initial_factors, + "...result initial factors is right"); + is_int(1, token->token.id.loa, "...result LoA is right"); + } + + /* + * Request an X.509 factor and try again. This should still work even + * though this user doesn't have password listed as a supported factor in + * the metadata. + */ + req.initial_factors = "x"; + status = webauth_webkdc_login(ctx, &request, &response, ring); + if (status != WA_ERR_NONE) + diag("error status: %s", webauth_error_message(ctx, status)); + is_int(WA_ERR_NONE, status, "Multifactor with proxy returns success"); + is_int(0, response->login_error, "...with no error"); + is_string(NULL, response->login_message, "...and no message"); + ok(response->result != NULL, "...there is a result token"); + is_string("id", response->result_type, "...which is an id token"); + + /* + * Change the WebKDC proxy token to assert just a password factor and ask + * for an OTP factor, and try again. This should be rejected with + * multifactor required. + */ + wkproxy.token.webkdc_proxy.initial_factors = "p"; + req.initial_factors = "o"; + status = webauth_webkdc_login(ctx, &request, &response, ring); + is_int(WA_ERR_NONE, status, "Multifactor without config returns success"); + is_int(WA_PEC_MULTIFACTOR_UNAVAILABLE, response->login_error, + "...with the right error"); + is_string("multifactor required but not configured", + response->login_message, "...and the right message"); + ok(response->result == NULL, "...and there is no result token"); + is_int(1, response->factors_wanted->nelts, "...and one factor is wanted"); + is_string("o", APR_ARRAY_IDX(response->factors_wanted, 0, const char *), + "...which is the OTP factor"); + is_int(1, response->factors_configured->nelts, + "...and one factor is configured"); + is_string("p", + APR_ARRAY_IDX(response->factors_configured, 0, const char *), + "...which is the password factor"); + + /* + * Try with a user who has multifactor configuration and forced + * multifactor. + */ + wkproxy.token.webkdc_proxy.subject = "full"; + wkproxy.token.webkdc_proxy.data = "full"; + wkproxy.token.webkdc_proxy.data_len = strlen("full"); + status = webauth_webkdc_login(ctx, &request, &response, ring); + is_int(WA_ERR_NONE, status, "Multifactor with config returns success"); + is_int(WA_PEC_MULTIFACTOR_REQUIRED, response->login_error, + "...with the right error"); + is_string("multifactor login required", response->login_message, + "...and the right message"); + ok(response->result == NULL, "...and there is no result token"); + is_int(2, response->factors_wanted->nelts, + "...and two factors are wanted"); + is_string("o", APR_ARRAY_IDX(response->factors_wanted, 0, const char *), + "...which is the OTP factor"); + is_int(5, response->factors_configured->nelts, + "...and five factors are configured"); + is_string("p", + APR_ARRAY_IDX(response->factors_configured, 0, const char *), + "...which is the password factor"); + is_string("r", + APR_ARRAY_IDX(response->factors_configured, 1, const char *), + "...the random factor"); + is_string("m", + APR_ARRAY_IDX(response->factors_configured, 2, const char *), + "...the generic multifactor factor"); + is_string("o", + APR_ARRAY_IDX(response->factors_configured, 3, const char *), + "...the OTP factor"); + is_string("o3", + APR_ARRAY_IDX(response->factors_configured, 4, const char *), + "...and the OTP-3 factor"); + + /* + * Add a second webkdc-proxy token that repesents an OTP login. This + * login should then work. + */ + wkproxy.token.webkdc_proxy.loa = 3; + wkproxy2.type = WA_TOKEN_WEBKDC_PROXY; + wkproxy2.token.webkdc_proxy.subject = "full"; + wkproxy2.token.webkdc_proxy.proxy_type = "remuser"; + wkproxy2.token.webkdc_proxy.proxy_subject = "WEBKDC:remuser"; + wkproxy2.token.webkdc_proxy.data = "full"; + wkproxy2.token.webkdc_proxy.data_len = strlen("full"); + wkproxy2.token.webkdc_proxy.initial_factors = "o,o3"; + wkproxy2.token.webkdc_proxy.loa = 2; + wkproxy2.token.webkdc_proxy.creation = now; + wkproxy2.token.webkdc_proxy.expiration = now + 30 * 60; + APR_ARRAY_PUSH(request.creds, struct webauth_token *) = &wkproxy2; + status = webauth_webkdc_login(ctx, &request, &response, ring); + is_int(WA_ERR_NONE, status, + "Multifactor with two proxies returns success"); + is_int(0, response->login_error, "...with no error"); + is_string(NULL, response->login_message, "...and no error message"); + ok(response->result != NULL, "...there is a result token"); + is_string("id", response->result_type, "...which is an id token"); + status = webauth_token_decode(ctx, WA_TOKEN_ID, response->result, + session, &token); + is_int(WA_ERR_NONE, status, "...result token decodes properly"); + if (status != WA_ERR_NONE) + ok_block(5, 0, "...no result token: %s", + webauth_error_message(ctx, status)); + else { + is_string("full", token->token.id.subject, + "...result subject is right"); + is_string("webkdc", token->token.id.auth, + "...result auth type is right"); + is_string("p,o,o3,m", token->token.proxy.initial_factors, + "...result initial factors is right"); + is_int(3, token->token.id.loa, "...result LoA is right"); + is_int(now + 30 * 60, token->token.id.expiration, + "...and expiration matches the shorter expiration"); + } + + /* Clean up. */ + remctld_stop(remctld); + kerberos_cleanup(); + apr_terminate(); + return 0; +} |