xref: /freebsd/crypto/krb5/src/plugins/certauth/test/main.c (revision 7f2fe78b9dd5f51c821d771b63d2e096f6fd49e9)
1 /* -*- mode: c; c-basic-offset: 4; indent-tabs-mode: nil -*- */
2 /* plugins/certauth/main.c - certauth plugin test modules. */
3 /*
4  * Copyright (C) 2017 by Red Hat, Inc.
5  * All rights reserved.
6  *
7  * Redistribution and use in source and binary forms, with or without
8  * modification, are permitted provided that the following conditions
9  * are met:
10  *
11  * * Redistributions of source code must retain the above copyright
12  *   notice, this list of conditions and the following disclaimer.
13  *
14  * * Redistributions in binary form must reproduce the above copyright
15  *   notice, this list of conditions and the following disclaimer in
16  *   the documentation and/or other materials provided with the
17  *   distribution.
18  *
19  * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS
20  * "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT
21  * LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS
22  * FOR A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE
23  * COPYRIGHT HOLDER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT,
24  * INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES
25  * (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR
26  * SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION)
27  * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT,
28  * STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE)
29  * ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED
30  * OF THE POSSIBILITY OF SUCH DAMAGE.
31  */
32 
33 #include <k5-int.h>
34 #include <kdb.h>
35 #include "krb5/certauth_plugin.h"
36 
37 struct krb5_certauth_moddata_st {
38     int initialized;
39 };
40 
41 /* Test module 1 passes with an indicator. */
42 static krb5_error_code
test1_authorize(krb5_context context,krb5_certauth_moddata moddata,const uint8_t * cert,size_t cert_len,krb5_const_principal princ,const void * opts,const struct _krb5_db_entry_new * db_entry,char *** authinds_out)43 test1_authorize(krb5_context context, krb5_certauth_moddata moddata,
44                 const uint8_t *cert, size_t cert_len,
45                 krb5_const_principal princ, const void *opts,
46                 const struct _krb5_db_entry_new *db_entry,
47                 char ***authinds_out)
48 {
49     char **ais = NULL;
50 
51     ais = calloc(2, sizeof(*ais));
52     assert(ais != NULL);
53     ais[0] = strdup("test1");
54     assert(ais[0] != NULL);
55     *authinds_out = ais;
56     return KRB5_PLUGIN_NO_HANDLE;
57 }
58 
59 static void
test_free_ind(krb5_context context,krb5_certauth_moddata moddata,char ** authinds)60 test_free_ind(krb5_context context, krb5_certauth_moddata moddata,
61               char **authinds)
62 {
63     size_t i;
64 
65     if (authinds == NULL)
66         return;
67     for (i = 0; authinds[i] != NULL; i++)
68         free(authinds[i]);
69     free(authinds);
70 }
71 
72 /* A basic moddata test. */
73 static krb5_error_code
test2_init(krb5_context context,krb5_certauth_moddata * moddata_out)74 test2_init(krb5_context context, krb5_certauth_moddata *moddata_out)
75 {
76     krb5_certauth_moddata mod;
77 
78     mod = calloc(1, sizeof(*mod));
79     assert(mod != NULL);
80     mod->initialized = 1;
81     *moddata_out = mod;
82     return 0;
83 }
84 
85 static void
test2_fini(krb5_context context,krb5_certauth_moddata moddata)86 test2_fini(krb5_context context, krb5_certauth_moddata moddata)
87 {
88     free(moddata);
89 }
90 
91 /* Return true if cert appears to contain the CN name, based on a search of the
92  * DER encoding. */
93 static krb5_boolean
has_cn(krb5_context context,const uint8_t * cert,size_t cert_len,const char * name)94 has_cn(krb5_context context, const uint8_t *cert, size_t cert_len,
95        const char *name)
96 {
97     krb5_boolean match = FALSE;
98     uint8_t name_len, cntag[5] = "\x06\x03\x55\x04\x03";
99     const uint8_t *c;
100     struct k5buf buf;
101     size_t c_left;
102 
103     /* Construct a DER search string of the CN AttributeType encoding followed
104      * by a UTF8String encoding containing name as the AttributeValue. */
105     k5_buf_init_dynamic(&buf);
106     k5_buf_add_len(&buf, cntag, sizeof(cntag));
107     k5_buf_add(&buf, "\x0C");
108     assert(strlen(name) < 128);
109     name_len = strlen(name);
110     k5_buf_add_len(&buf, &name_len, 1);
111     k5_buf_add_len(&buf, name, name_len);
112     assert(k5_buf_status(&buf) == 0);
113 
114     /* Check for the CN needle in the certificate haystack. */
115     c_left = cert_len;
116     c = memchr(cert, *cntag, c_left);
117     while (c != NULL) {
118         c_left = cert_len - (c - cert);
119         if (buf.len > c_left)
120             break;
121         if (memcmp(c, buf.data, buf.len) == 0) {
122             match = TRUE;
123             break;
124         }
125         assert(c_left >= 1);
126         c = memchr(c + 1, *cntag, c_left - 1);
127     }
128 
129     k5_buf_free(&buf);
130     return match;
131 }
132 
133 /*
134  * Test module 2 passes if the principal name is "nocert".  Otherwise it
135  * returns OK if the CN of the cert matches the principal name, with indicators
136  * of the module name and princ, and errors if the certificate does not match.
137  */
138 static krb5_error_code
test2_authorize(krb5_context context,krb5_certauth_moddata moddata,const uint8_t * cert,size_t cert_len,krb5_const_principal princ,const void * opts,const struct _krb5_db_entry_new * db_entry,char *** authinds_out)139 test2_authorize(krb5_context context, krb5_certauth_moddata moddata,
140                 const uint8_t *cert, size_t cert_len,
141                 krb5_const_principal princ, const void *opts,
142                 const struct _krb5_db_entry_new *db_entry,
143                 char ***authinds_out)
144 {
145     krb5_error_code ret;
146     char *name = NULL, **ais = NULL;
147 
148     *authinds_out = NULL;
149 
150     assert(moddata != NULL && moddata->initialized);
151 
152     ret = krb5_unparse_name_flags(context, princ,
153                                   KRB5_PRINCIPAL_UNPARSE_NO_REALM, &name);
154     if (ret)
155         goto cleanup;
156     if (strcmp(name, "nocert") == 0) {
157         ret = KRB5_PLUGIN_NO_HANDLE;
158         goto cleanup;
159     }
160 
161     if (!has_cn(context, cert, cert_len, name)) {
162         ret = KRB5KDC_ERR_CERTIFICATE_MISMATCH;
163         goto cleanup;
164     }
165 
166     /* Create an indicator list with the module name and CN. */
167     ais = calloc(3, sizeof(*ais));
168     assert(ais != NULL);
169     ais[0] = strdup("test2");
170     ais[1] = strdup(name);
171     assert(ais[0] != NULL && ais[1] != NULL);
172     *authinds_out = ais;
173 
174     ais = NULL;
175 
176 cleanup:
177     krb5_free_unparsed_name(context, name);
178     return ret;
179 }
180 
181 /*
182  * Test module 3 reads the "hwauth" string attribute on db_entry, and adds an
183  * authentication indicator of "hwauth:<value>" if the attribute is set.  It
184  * returns KRB5_CERTAUTH_HWAUTH if the value is "ok", and
185  * KRB5_CERTAUTH_HWAUTH_PASS if the value is "pass".  Otherwise it passes.
186  */
187 static krb5_error_code
test3_authorize(krb5_context context,krb5_certauth_moddata moddata,const uint8_t * cert,size_t cert_len,krb5_const_principal princ,const void * opts,const struct _krb5_db_entry_new * db_entry,char *** authinds_out)188 test3_authorize(krb5_context context, krb5_certauth_moddata moddata,
189                 const uint8_t *cert, size_t cert_len,
190                 krb5_const_principal princ, const void *opts,
191                 const struct _krb5_db_entry_new *db_entry,
192                 char ***authinds_out)
193 {
194     krb5_error_code ret;
195     char *strval = NULL, **ais = NULL;
196 
197     ret = krb5_dbe_get_string(context, (krb5_db_entry *)db_entry, "hwauth",
198                               &strval);
199     if (ret)
200         return ret;
201     if (strval != NULL && strcmp(strval, "ok") == 0)
202         ret = KRB5_CERTAUTH_HWAUTH;
203     else if (strval != NULL && strcmp(strval, "pass") == 0)
204         ret = KRB5_CERTAUTH_HWAUTH_PASS;
205     else
206         ret = KRB5_PLUGIN_NO_HANDLE;
207 
208     if (strval != NULL) {
209         ais = calloc(2, sizeof(*ais));
210         assert(ais != NULL);
211         if (asprintf(&ais[0], "hwauth:%s", strval) < 0)
212             abort();
213         assert(ais[0] != NULL);
214     }
215 
216     *authinds_out = ais;
217     ais = NULL;
218 
219     krb5_dbe_free_string(context, strval);
220     return ret;
221 }
222 
223 krb5_error_code
224 certauth_test1_initvt(krb5_context context, int maj_ver, int min_ver,
225                       krb5_plugin_vtable vtable);
226 krb5_error_code
certauth_test1_initvt(krb5_context context,int maj_ver,int min_ver,krb5_plugin_vtable vtable)227 certauth_test1_initvt(krb5_context context, int maj_ver, int min_ver,
228                       krb5_plugin_vtable vtable)
229 {
230     krb5_certauth_vtable vt;
231 
232     if (maj_ver != 1)
233         return KRB5_PLUGIN_VER_NOTSUPP;
234     vt = (krb5_certauth_vtable)vtable;
235     vt->name = "test1";
236     vt->authorize = test1_authorize;
237     vt->free_ind = test_free_ind;
238     return 0;
239 }
240 
241 krb5_error_code
242 certauth_test2_initvt(krb5_context context, int maj_ver, int min_ver,
243                       krb5_plugin_vtable vtable);
244 krb5_error_code
certauth_test2_initvt(krb5_context context,int maj_ver,int min_ver,krb5_plugin_vtable vtable)245 certauth_test2_initvt(krb5_context context, int maj_ver, int min_ver,
246                       krb5_plugin_vtable vtable)
247 {
248     krb5_certauth_vtable vt;
249 
250     if (maj_ver != 1)
251         return KRB5_PLUGIN_VER_NOTSUPP;
252     vt = (krb5_certauth_vtable)vtable;
253     vt->name = "test2";
254     vt->authorize = test2_authorize;
255     vt->init = test2_init;
256     vt->fini = test2_fini;
257     vt->free_ind = test_free_ind;
258     return 0;
259 }
260 
261 krb5_error_code
262 certauth_test3_initvt(krb5_context context, int maj_ver, int min_ver,
263                       krb5_plugin_vtable vtable);
264 
265 krb5_error_code
certauth_test3_initvt(krb5_context context,int maj_ver,int min_ver,krb5_plugin_vtable vtable)266 certauth_test3_initvt(krb5_context context, int maj_ver, int min_ver,
267                       krb5_plugin_vtable vtable)
268 {
269     krb5_certauth_vtable vt;
270 
271     if (maj_ver != 1)
272         return KRB5_PLUGIN_VER_NOTSUPP;
273     vt = (krb5_certauth_vtable)vtable;
274     vt->name = "test3";
275     vt->authorize = test3_authorize;
276     vt->free_ind = test_free_ind;
277     return 0;
278 }
279