aboutsummaryrefslogtreecommitdiff
path: root/src/plugins/preauth/test/cltest.c
blob: f5f7c5aba167178a7be911eec318a92a74c4882e (plain) (blame)
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
166
167
168
169
170
171
172
173
174
175
176
177
178
179
180
181
182
183
184
185
186
187
188
189
190
191
192
193
194
195
196
197
198
199
200
201
202
203
204
205
206
207
208
209
210
211
212
213
214
215
216
217
218
219
220
221
222
223
224
225
226
227
228
229
230
231
232
233
234
235
236
237
238
239
240
241
242
/* -*- mode: c; c-basic-offset: 4; indent-tabs-mode: nil -*- */
/* plugins/preauth/test/cltest.c - Test clpreauth module */
/*
 * Copyright (C) 2015, 2017 by the Massachusetts Institute of Technology.
 * All rights reserved.
 *
 * Redistribution and use in source and binary forms, with or without
 * modification, are permitted provided that the following conditions
 * are met:
 *
 * * Redistributions of source code must retain the above copyright
 *   notice, this list of conditions and the following disclaimer.
 *
 * * Redistributions in binary form must reproduce the above copyright
 *   notice, this list of conditions and the following disclaimer in
 *   the documentation and/or other materials provided with the
 *   distribution.
 *
 * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS
 * "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT
 * LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS
 * FOR A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE
 * COPYRIGHT HOLDER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT,
 * INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES
 * (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR
 * SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION)
 * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT,
 * STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE)
 * ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED
 * OF THE POSSIBILITY OF SUCH DAMAGE.
 */

/*
 * This module is used to test preauth interface features.  At this time, the
 * clpreauth module does the following:
 *
 * - It decrypts a message from the initial KDC pa-data using the reply key and
 *   prints it to stdout.  (The unencrypted message "no key" can also be
 *   displayed.)
 *
 * - If a second round trip is requested, it prints the pa-data contents
 *   accompanying the second round trip request.
 *
 * - It pulls an "indicators" attribute from the gic preauth options and sends
 *   it to the server, instructing the kdcpreauth module to assert one or more
 *   space-separated authentication indicators.  (This string is sent on both
 *   round trips if a second round trip is requested.)
 *
 * - If a KDC_ERR_ENCTYPE_NOSUPP error with e-data is received, it prints the
 *   accompanying error padata and sends a follow-up request containing
 *   "tryagain".
 *
 * - If the "fail_optimistic", "fail_2rt", or "fail_tryagain" gic options are
 *   set, it fails with a recognizable error string at the requested point in
 *   processing.
 */

#include "k5-int.h"
#include <krb5/clpreauth_plugin.h>
#include "common.h"

static krb5_preauthtype pa_types[] = { TEST_PA_TYPE, 0 };

struct client_state {
    char *indicators;
    krb5_boolean fail_optimistic;
    krb5_boolean fail_2rt;
    krb5_boolean fail_tryagain;
};

struct client_request_state {
    krb5_boolean second_round_trip;
};

static krb5_error_code
test_init(krb5_context context, krb5_clpreauth_moddata *moddata_out)
{
    struct client_state *st;

    st = malloc(sizeof(*st));
    assert(st != NULL);
    st->indicators = NULL;
    st->fail_optimistic = st->fail_2rt = st->fail_tryagain = FALSE;
    *moddata_out = (krb5_clpreauth_moddata)st;
    return 0;
}

static void
test_fini(krb5_context context, krb5_clpreauth_moddata moddata)
{
    struct client_state *st = (struct client_state *)moddata;

    free(st->indicators);
    free(st);
}

static void
test_request_init(krb5_context context, krb5_clpreauth_moddata moddata,
                  krb5_clpreauth_modreq *modreq_out)
{
    struct client_request_state *reqst;

    reqst = malloc(sizeof(*reqst));
    assert(reqst != NULL);
    reqst->second_round_trip = FALSE;
    *modreq_out = (krb5_clpreauth_modreq)reqst;
}

static void
test_request_fini(krb5_context context, krb5_clpreauth_moddata moddata,
                  krb5_clpreauth_modreq modreq)
{
    free(modreq);
}

static krb5_error_code
test_process(krb5_context context, krb5_clpreauth_moddata moddata,
             krb5_clpreauth_modreq modreq, krb5_get_init_creds_opt *opt,
             krb5_clpreauth_callbacks cb, krb5_clpreauth_rock rock,
             krb5_kdc_req *request, krb5_data *encoded_request_body,
             krb5_data *encoded_previous_request, krb5_pa_data *pa_data,
             krb5_prompter_fct prompter, void *prompter_data,
             krb5_pa_data ***out_pa_data)
{
    struct client_state *st = (struct client_state *)moddata;
    struct client_request_state *reqst = (struct client_request_state *)modreq;
    krb5_error_code ret;
    krb5_keyblock *k;
    krb5_enc_data enc;
    krb5_data plain;
    const char *indstr;

    if (pa_data->length == 0) {
        /* This is an optimistic preauth test.  Send a recognizable padata
         * value so the KDC knows not to expect a cookie. */
        if (st->fail_optimistic) {
            k5_setmsg(context, KRB5_PREAUTH_FAILED, "induced optimistic fail");
            return KRB5_PREAUTH_FAILED;
        }
        *out_pa_data = make_pa_list("optimistic", 10);
        return 0;
    } else if (reqst->second_round_trip) {
        printf("2rt: %.*s\n", pa_data->length, pa_data->contents);
        if (st->fail_2rt) {
            k5_setmsg(context, KRB5_PREAUTH_FAILED, "induced 2rt fail");
            return KRB5_PREAUTH_FAILED;
        }
    } else if (pa_data->length == 6 &&
               memcmp(pa_data->contents, "no key", 6) == 0) {
        printf("no key\n");
    } else {
        /* This fails during s4u_identify_user(), so don't assert. */
        ret = cb->get_as_key(context, rock, &k);
        if (ret)
            return ret;
        ret = alloc_data(&plain, pa_data->length);
        assert(!ret);
        enc.enctype = k->enctype;
        enc.ciphertext = make_data(pa_data->contents, pa_data->length);
        ret = krb5_c_decrypt(context, k, 1024, NULL, &enc, &plain);
        assert(!ret);
        printf("%.*s\n", plain.length, plain.data);
        free(plain.data);
    }
    reqst->second_round_trip = TRUE;

    indstr = (st->indicators != NULL) ? st->indicators : "";
    *out_pa_data = make_pa_list(indstr, strlen(indstr));
    return 0;
}

static krb5_error_code
test_tryagain(krb5_context context, krb5_clpreauth_moddata moddata,
              krb5_clpreauth_modreq modreq, krb5_get_init_creds_opt *opt,
              krb5_clpreauth_callbacks cb, krb5_clpreauth_rock rock,
              krb5_kdc_req *request, krb5_data *enc_req, krb5_data *enc_prev,
              krb5_preauthtype pa_type, krb5_error *error,
              krb5_pa_data **padata, krb5_prompter_fct prompter,
              void *prompter_data, krb5_pa_data ***padata_out)
{
    struct client_state *st = (struct client_state *)moddata;
    int i;

    *padata_out = NULL;
    if (st->fail_tryagain) {
        k5_setmsg(context, KRB5_PREAUTH_FAILED, "induced tryagain fail");
        return KRB5_PREAUTH_FAILED;
    }
    if (error->error != KDC_ERR_ENCTYPE_NOSUPP)
        return KRB5_PREAUTH_FAILED;
    for (i = 0; padata[i] != NULL; i++) {
        if (padata[i]->pa_type == TEST_PA_TYPE)
            printf("tryagain: %.*s\n", padata[i]->length, padata[i]->contents);
    }
    *padata_out = make_pa_list("tryagain", 8);
    return 0;
}

static krb5_error_code
test_gic_opt(krb5_context kcontext, krb5_clpreauth_moddata moddata,
             krb5_get_init_creds_opt *opt, const char *attr, const char *value)
{
    struct client_state *st = (struct client_state *)moddata;

    if (strcmp(attr, "indicators") == 0) {
        free(st->indicators);
        st->indicators = strdup(value);
        assert(st->indicators != NULL);
    } else if (strcmp(attr, "fail_optimistic") == 0) {
        st->fail_optimistic = TRUE;
    } else if (strcmp(attr, "fail_2rt") == 0) {
        st->fail_2rt = TRUE;
    } else if (strcmp(attr, "fail_tryagain") == 0) {
        st->fail_tryagain = TRUE;
    }
    return 0;
}

krb5_error_code
clpreauth_test_initvt(krb5_context context, int maj_ver,
                            int min_ver, krb5_plugin_vtable vtable);

krb5_error_code
clpreauth_test_initvt(krb5_context context, int maj_ver,
                            int min_ver, krb5_plugin_vtable vtable)
{
    krb5_clpreauth_vtable vt;

    if (maj_ver != 1)
        return KRB5_PLUGIN_VER_NOTSUPP;
    vt = (krb5_clpreauth_vtable)vtable;
    vt->name = "test";
    vt->pa_type_list = pa_types;
    vt->init = test_init;
    vt->fini = test_fini;
    vt->request_init = test_request_init;
    vt->request_fini = test_request_fini;
    vt->process = test_process;
    vt->tryagain = test_tryagain;
    vt->gic_opts = test_gic_opt;
    return 0;
}