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