1 /* -*- mode: c; c-basic-offset: 4; indent-tabs-mode: nil -*- */
2 #include "k5-int.h"
3 #include "int-proto.h"
4 #include <krb5/clpreauth_plugin.h>
5
6 #define GIC_OPT_EXTENDED 0x80000000
7 #define GIC_OPT_SHALLOW_COPY 0x40000000
8
9 #define DEFAULT_FLAGS KRB5_GET_INIT_CREDS_OPT_CHG_PWD_PRMPT
10
11 #if defined(__MACH__) && defined(__APPLE__)
12 #include <TargetConditionals.h>
13 #endif
14
15 /* Match struct packing of krb5_get_init_creds_opt on macOS. */
16 #if defined(__APPLE__) && (defined(__ppc__) || defined(__ppc64__) || defined(__i386__) || defined(__x86_64__))
17 #pragma pack(push,2)
18 #endif
19 struct extended_options {
20 krb5_get_init_creds_opt opt;
21 int num_preauth_data;
22 krb5_gic_opt_pa_data *preauth_data;
23 char *fast_ccache_name;
24 krb5_ccache in_ccache;
25 krb5_ccache out_ccache;
26 krb5_flags fast_flags;
27 krb5_expire_callback_func expire_cb;
28 void *expire_data;
29 krb5_responder_fn responder;
30 void *responder_data;
31 int pac_request; /* -1 unset, 0 false, 1 true */
32 };
33 #if defined(__APPLE__) && (defined(__ppc__) || defined(__ppc64__) || defined(__i386__) || defined(__x86_64__))
34 #pragma pack(pop)
35 #endif
36
37 void KRB5_CALLCONV
krb5_get_init_creds_opt_init(krb5_get_init_creds_opt * opt)38 krb5_get_init_creds_opt_init(krb5_get_init_creds_opt *opt)
39 {
40 opt->flags = DEFAULT_FLAGS;
41 }
42
43 void KRB5_CALLCONV
krb5_get_init_creds_opt_set_tkt_life(krb5_get_init_creds_opt * opt,krb5_deltat tkt_life)44 krb5_get_init_creds_opt_set_tkt_life(krb5_get_init_creds_opt *opt,
45 krb5_deltat tkt_life)
46 {
47 opt->flags |= KRB5_GET_INIT_CREDS_OPT_TKT_LIFE;
48 opt->tkt_life = tkt_life;
49 }
50
51 void KRB5_CALLCONV
krb5_get_init_creds_opt_set_renew_life(krb5_get_init_creds_opt * opt,krb5_deltat renew_life)52 krb5_get_init_creds_opt_set_renew_life(krb5_get_init_creds_opt *opt,
53 krb5_deltat renew_life)
54 {
55 opt->flags |= KRB5_GET_INIT_CREDS_OPT_RENEW_LIFE;
56 opt->renew_life = renew_life;
57 }
58
59 void KRB5_CALLCONV
krb5_get_init_creds_opt_set_forwardable(krb5_get_init_creds_opt * opt,int forwardable)60 krb5_get_init_creds_opt_set_forwardable(krb5_get_init_creds_opt *opt,
61 int forwardable)
62 {
63 opt->flags |= KRB5_GET_INIT_CREDS_OPT_FORWARDABLE;
64 opt->forwardable = forwardable;
65 }
66
67 void KRB5_CALLCONV
krb5_get_init_creds_opt_set_proxiable(krb5_get_init_creds_opt * opt,int proxiable)68 krb5_get_init_creds_opt_set_proxiable(krb5_get_init_creds_opt *opt,
69 int proxiable)
70 {
71 opt->flags |= KRB5_GET_INIT_CREDS_OPT_PROXIABLE;
72 opt->proxiable = proxiable;
73 }
74
75 void KRB5_CALLCONV
krb5_get_init_creds_opt_set_canonicalize(krb5_get_init_creds_opt * opt,int canonicalize)76 krb5_get_init_creds_opt_set_canonicalize(krb5_get_init_creds_opt *opt,
77 int canonicalize)
78 {
79 if (canonicalize)
80 opt->flags |= KRB5_GET_INIT_CREDS_OPT_CANONICALIZE;
81 else
82 opt->flags &= ~(KRB5_GET_INIT_CREDS_OPT_CANONICALIZE);
83 }
84
85 void KRB5_CALLCONV
krb5_get_init_creds_opt_set_anonymous(krb5_get_init_creds_opt * opt,int anonymous)86 krb5_get_init_creds_opt_set_anonymous (krb5_get_init_creds_opt *opt,
87 int anonymous)
88 {
89 if (anonymous)
90 opt->flags |= KRB5_GET_INIT_CREDS_OPT_ANONYMOUS;
91 else opt->flags &= ~KRB5_GET_INIT_CREDS_OPT_ANONYMOUS;
92 }
93
94 void KRB5_CALLCONV
krb5_get_init_creds_opt_set_etype_list(krb5_get_init_creds_opt * opt,krb5_enctype * etype_list,int etype_list_length)95 krb5_get_init_creds_opt_set_etype_list(krb5_get_init_creds_opt *opt, krb5_enctype *etype_list, int etype_list_length)
96 {
97 opt->flags |= KRB5_GET_INIT_CREDS_OPT_ETYPE_LIST;
98 opt->etype_list = etype_list;
99 opt->etype_list_length = etype_list_length;
100 }
101
102 void KRB5_CALLCONV
krb5_get_init_creds_opt_set_address_list(krb5_get_init_creds_opt * opt,krb5_address ** addresses)103 krb5_get_init_creds_opt_set_address_list(krb5_get_init_creds_opt *opt,
104 krb5_address **addresses)
105 {
106 opt->flags |= KRB5_GET_INIT_CREDS_OPT_ADDRESS_LIST;
107 opt->address_list = addresses;
108 }
109
110 void KRB5_CALLCONV
krb5_get_init_creds_opt_set_preauth_list(krb5_get_init_creds_opt * opt,krb5_preauthtype * preauth_list,int preauth_list_length)111 krb5_get_init_creds_opt_set_preauth_list(krb5_get_init_creds_opt *opt,
112 krb5_preauthtype *preauth_list,
113 int preauth_list_length)
114 {
115 opt->flags |= KRB5_GET_INIT_CREDS_OPT_PREAUTH_LIST;
116 opt->preauth_list = preauth_list;
117 opt->preauth_list_length = preauth_list_length;
118 }
119
120 void KRB5_CALLCONV
krb5_get_init_creds_opt_set_salt(krb5_get_init_creds_opt * opt,krb5_data * salt)121 krb5_get_init_creds_opt_set_salt(krb5_get_init_creds_opt *opt, krb5_data *salt)
122 {
123 opt->flags |= KRB5_GET_INIT_CREDS_OPT_SALT;
124 opt->salt = salt;
125 }
126
127 void KRB5_CALLCONV
krb5_get_init_creds_opt_set_change_password_prompt(krb5_get_init_creds_opt * opt,int prompt)128 krb5_get_init_creds_opt_set_change_password_prompt(
129 krb5_get_init_creds_opt *opt, int prompt)
130 {
131 if (prompt)
132 opt->flags |= KRB5_GET_INIT_CREDS_OPT_CHG_PWD_PRMPT;
133 else
134 opt->flags &= ~KRB5_GET_INIT_CREDS_OPT_CHG_PWD_PRMPT;
135 }
136
137 krb5_error_code KRB5_CALLCONV
krb5_get_init_creds_opt_alloc(krb5_context context,krb5_get_init_creds_opt ** opt)138 krb5_get_init_creds_opt_alloc(krb5_context context,
139 krb5_get_init_creds_opt **opt)
140 {
141 struct extended_options *opte;
142
143 if (opt == NULL)
144 return EINVAL;
145 *opt = NULL;
146
147 /* Return an extended structure cast as a krb5_get_init_creds_opt. */
148 opte = calloc(1, sizeof(*opte));
149 if (opte == NULL)
150 return ENOMEM;
151 opte->opt.flags = DEFAULT_FLAGS | GIC_OPT_EXTENDED;
152 opte->pac_request = -1;
153 *opt = (krb5_get_init_creds_opt *)opte;
154 return 0;
155 }
156
157 void KRB5_CALLCONV
krb5_get_init_creds_opt_free(krb5_context context,krb5_get_init_creds_opt * opt)158 krb5_get_init_creds_opt_free(krb5_context context,
159 krb5_get_init_creds_opt *opt)
160 {
161 struct extended_options *opte = (struct extended_options *)opt;
162 int i;
163
164 if (opt == NULL || !(opt->flags & GIC_OPT_EXTENDED))
165 return;
166 assert(!(opt->flags & GIC_OPT_SHALLOW_COPY));
167 for (i = 0; i < opte->num_preauth_data; i++) {
168 free(opte->preauth_data[i].attr);
169 free(opte->preauth_data[i].value);
170 }
171 free(opte->preauth_data);
172 free(opte->fast_ccache_name);
173 free(opte);
174 }
175
176 krb5_error_code KRB5_CALLCONV
krb5_get_init_creds_opt_set_pa(krb5_context context,krb5_get_init_creds_opt * opt,const char * attr,const char * value)177 krb5_get_init_creds_opt_set_pa(krb5_context context,
178 krb5_get_init_creds_opt *opt,
179 const char *attr,
180 const char *value)
181 {
182 struct extended_options *opte = (struct extended_options *)opt;
183 krb5_gic_opt_pa_data *t, *pa;
184
185 if (opt == NULL || !(opt->flags & GIC_OPT_EXTENDED))
186 return EINVAL;
187 assert(!(opt->flags & GIC_OPT_SHALLOW_COPY));
188
189 /* Allocate space for another option. */
190 t = realloc(opte->preauth_data, (opte->num_preauth_data + 1) * sizeof(*t));
191 if (t == NULL)
192 return ENOMEM;
193 opte->preauth_data = t;
194
195 /* Copy the option into the new slot. */
196 pa = &opte->preauth_data[opte->num_preauth_data];
197 pa->attr = strdup(attr);
198 if (pa->attr == NULL)
199 return ENOMEM;
200 pa->value = strdup(value);
201 if (pa->value == NULL) {
202 free(pa->attr);
203 return ENOMEM;
204 }
205 opte->num_preauth_data++;
206
207 /* Give preauth modules a chance to look at the option now. */
208 return krb5_preauth_supply_preauth_data(context, opt, attr, value);
209 }
210
211 /*
212 * This function allows a preauth plugin to obtain preauth
213 * options. The preauth_data returned from this function
214 * should be freed by calling krb5_get_init_creds_opt_free_pa().
215 *
216 * The 'opt' pointer supplied to this function must have been
217 * obtained using krb5_get_init_creds_opt_alloc()
218 */
219 krb5_error_code KRB5_CALLCONV
krb5_get_init_creds_opt_get_pa(krb5_context context,krb5_get_init_creds_opt * opt,int * num_preauth_data,krb5_gic_opt_pa_data ** preauth_data)220 krb5_get_init_creds_opt_get_pa(krb5_context context,
221 krb5_get_init_creds_opt *opt,
222 int *num_preauth_data,
223 krb5_gic_opt_pa_data **preauth_data)
224 {
225 struct extended_options *opte = (struct extended_options *)opt;
226 krb5_gic_opt_pa_data *p = NULL;
227 int i;
228
229 if (num_preauth_data == NULL || preauth_data == NULL)
230 return EINVAL;
231 *num_preauth_data = 0;
232 *preauth_data = NULL;
233 if (opt == NULL || !(opt->flags & GIC_OPT_EXTENDED))
234 return EINVAL;
235
236 if (opte->num_preauth_data == 0)
237 return 0;
238
239 p = calloc(opte->num_preauth_data, sizeof(*p));
240 if (p == NULL)
241 return ENOMEM;
242
243 for (i = 0; i < opte->num_preauth_data; i++) {
244 p[i].attr = strdup(opte->preauth_data[i].attr);
245 p[i].value = strdup(opte->preauth_data[i].value);
246 if (p[i].attr == NULL || p[i].value == NULL)
247 goto cleanup;
248 }
249 *num_preauth_data = i;
250 *preauth_data = p;
251 return 0;
252
253 cleanup:
254 krb5_get_init_creds_opt_free_pa(context, opte->num_preauth_data, p);
255 return ENOMEM;
256 }
257
258 /*
259 * This function frees the preauth_data that was returned by
260 * krb5_get_init_creds_opt_get_pa().
261 */
262 void KRB5_CALLCONV
krb5_get_init_creds_opt_free_pa(krb5_context context,int num_preauth_data,krb5_gic_opt_pa_data * preauth_data)263 krb5_get_init_creds_opt_free_pa(krb5_context context, int num_preauth_data,
264 krb5_gic_opt_pa_data *preauth_data)
265 {
266 int i;
267
268 if (num_preauth_data <= 0 || preauth_data == NULL)
269 return;
270
271 for (i = 0; i < num_preauth_data; i++) {
272 free(preauth_data[i].attr);
273 free(preauth_data[i].value);
274 }
275 free(preauth_data);
276 }
277
278 krb5_error_code KRB5_CALLCONV
krb5_get_init_creds_opt_set_fast_ccache_name(krb5_context context,krb5_get_init_creds_opt * opt,const char * ccache_name)279 krb5_get_init_creds_opt_set_fast_ccache_name(krb5_context context,
280 krb5_get_init_creds_opt *opt,
281 const char *ccache_name)
282 {
283 struct extended_options *opte = (struct extended_options *)opt;
284
285 if (opt == NULL || !(opt->flags & GIC_OPT_EXTENDED))
286 return EINVAL;
287 assert(!(opt->flags & GIC_OPT_SHALLOW_COPY));
288 free(opte->fast_ccache_name);
289 opte->fast_ccache_name = strdup(ccache_name);
290 if (opte->fast_ccache_name == NULL)
291 return ENOMEM;
292 return 0;
293 }
294
295 krb5_error_code KRB5_CALLCONV
krb5_get_init_creds_opt_set_fast_ccache(krb5_context context,krb5_get_init_creds_opt * opt,krb5_ccache ccache)296 krb5_get_init_creds_opt_set_fast_ccache(krb5_context context,
297 krb5_get_init_creds_opt *opt,
298 krb5_ccache ccache)
299 {
300 krb5_error_code ret;
301 char *name;
302
303 ret = krb5_cc_get_full_name(context, ccache, &name);
304 if (ret)
305 return ret;
306 ret = krb5_get_init_creds_opt_set_fast_ccache_name(context, opt, name);
307 free(name);
308 return ret;
309 }
310
311 const char *
k5_gic_opt_get_fast_ccache_name(krb5_get_init_creds_opt * opt)312 k5_gic_opt_get_fast_ccache_name(krb5_get_init_creds_opt *opt)
313 {
314 struct extended_options *opte = (struct extended_options *)opt;
315
316 if (opt == NULL || !(opt->flags & GIC_OPT_EXTENDED))
317 return NULL;
318 return opte->fast_ccache_name;
319 }
320
321 krb5_error_code KRB5_CALLCONV
krb5_get_init_creds_opt_set_in_ccache(krb5_context context,krb5_get_init_creds_opt * opt,krb5_ccache ccache)322 krb5_get_init_creds_opt_set_in_ccache(krb5_context context,
323 krb5_get_init_creds_opt *opt,
324 krb5_ccache ccache)
325 {
326 struct extended_options *opte = (struct extended_options *)opt;
327
328 if (opt == NULL || !(opt->flags & GIC_OPT_EXTENDED))
329 return EINVAL;
330 opte->in_ccache = ccache;
331 return 0;
332 }
333
334 krb5_ccache
k5_gic_opt_get_in_ccache(krb5_get_init_creds_opt * opt)335 k5_gic_opt_get_in_ccache(krb5_get_init_creds_opt *opt)
336 {
337 struct extended_options *opte = (struct extended_options *)opt;
338
339 if (opt == NULL || !(opt->flags & GIC_OPT_EXTENDED))
340 return NULL;
341 return opte->in_ccache;
342 }
343
344 krb5_error_code KRB5_CALLCONV
krb5_get_init_creds_opt_set_out_ccache(krb5_context context,krb5_get_init_creds_opt * opt,krb5_ccache ccache)345 krb5_get_init_creds_opt_set_out_ccache(krb5_context context,
346 krb5_get_init_creds_opt *opt,
347 krb5_ccache ccache)
348 {
349 struct extended_options *opte = (struct extended_options *)opt;
350
351 if (opt == NULL || !(opt->flags & GIC_OPT_EXTENDED))
352 return EINVAL;
353 opte->out_ccache = ccache;
354 return 0;
355 }
356
357 krb5_ccache
k5_gic_opt_get_out_ccache(krb5_get_init_creds_opt * opt)358 k5_gic_opt_get_out_ccache(krb5_get_init_creds_opt *opt)
359 {
360 struct extended_options *opte = (struct extended_options *)opt;
361
362 if (opt == NULL || !(opt->flags & GIC_OPT_EXTENDED))
363 return NULL;
364 return opte->out_ccache;
365 }
366
367 krb5_error_code KRB5_CALLCONV
krb5_get_init_creds_opt_set_fast_flags(krb5_context context,krb5_get_init_creds_opt * opt,krb5_flags flags)368 krb5_get_init_creds_opt_set_fast_flags(krb5_context context,
369 krb5_get_init_creds_opt *opt,
370 krb5_flags flags)
371 {
372 struct extended_options *opte = (struct extended_options *)opt;
373
374 if (opt == NULL || !(opt->flags & GIC_OPT_EXTENDED))
375 return EINVAL;
376 opte->fast_flags = flags;
377 return 0;
378 }
379
380 krb5_error_code KRB5_CALLCONV
krb5_get_init_creds_opt_get_fast_flags(krb5_context context,krb5_get_init_creds_opt * opt,krb5_flags * out_flags)381 krb5_get_init_creds_opt_get_fast_flags(krb5_context context,
382 krb5_get_init_creds_opt *opt,
383 krb5_flags *out_flags)
384 {
385 struct extended_options *opte = (struct extended_options *)opt;
386
387 if (out_flags == NULL)
388 return EINVAL;
389 *out_flags = 0;
390 if (opt == NULL || !(opt->flags & GIC_OPT_EXTENDED))
391 return EINVAL;
392 *out_flags = opte->fast_flags;
393 return 0;
394 }
395
396 krb5_flags
k5_gic_opt_get_fast_flags(krb5_get_init_creds_opt * opt)397 k5_gic_opt_get_fast_flags(krb5_get_init_creds_opt *opt)
398 {
399 struct extended_options *opte = (struct extended_options *)opt;
400
401 if (opt == NULL || !(opt->flags & GIC_OPT_EXTENDED))
402 return 0;
403 return opte->fast_flags;
404 }
405
406 krb5_error_code KRB5_CALLCONV
krb5_get_init_creds_opt_set_expire_callback(krb5_context context,krb5_get_init_creds_opt * opt,krb5_expire_callback_func cb,void * data)407 krb5_get_init_creds_opt_set_expire_callback(krb5_context context,
408 krb5_get_init_creds_opt *opt,
409 krb5_expire_callback_func cb,
410 void *data)
411 {
412 struct extended_options *opte = (struct extended_options *)opt;
413
414 if (opt == NULL || !(opt->flags & GIC_OPT_EXTENDED))
415 return EINVAL;
416 opte->expire_cb = cb;
417 opte->expire_data = data;
418 return 0;
419 }
420
421 void
k5_gic_opt_get_expire_cb(krb5_get_init_creds_opt * opt,krb5_expire_callback_func * cb_out,void ** data_out)422 k5_gic_opt_get_expire_cb(krb5_get_init_creds_opt *opt,
423 krb5_expire_callback_func *cb_out, void **data_out)
424 {
425 struct extended_options *opte = (struct extended_options *)opt;
426
427 *cb_out = NULL;
428 *data_out = NULL;
429 if (opt == NULL || !(opt->flags & GIC_OPT_EXTENDED))
430 return;
431 *cb_out = opte->expire_cb;
432 *data_out = opte->expire_data;
433 }
434
435 krb5_error_code KRB5_CALLCONV
krb5_get_init_creds_opt_set_responder(krb5_context context,krb5_get_init_creds_opt * opt,krb5_responder_fn responder,void * data)436 krb5_get_init_creds_opt_set_responder(krb5_context context,
437 krb5_get_init_creds_opt *opt,
438 krb5_responder_fn responder, void *data)
439 {
440 struct extended_options *opte = (struct extended_options *)opt;
441
442 if (opt == NULL || !(opt->flags & GIC_OPT_EXTENDED))
443 return EINVAL;
444 opte->responder = responder;
445 opte->responder_data = data;
446 return 0;
447 }
448
449 void
k5_gic_opt_get_responder(krb5_get_init_creds_opt * opt,krb5_responder_fn * responder_out,void ** data_out)450 k5_gic_opt_get_responder(krb5_get_init_creds_opt *opt,
451 krb5_responder_fn *responder_out, void **data_out)
452 {
453 struct extended_options *opte = (struct extended_options *)opt;
454
455 *responder_out = NULL;
456 *data_out = NULL;
457 if (opt == NULL || !(opt->flags & GIC_OPT_EXTENDED))
458 return;
459 *responder_out = opte->responder;
460 *data_out = opte->responder_data;
461 }
462
463 krb5_get_init_creds_opt *
k5_gic_opt_shallow_copy(krb5_get_init_creds_opt * opt)464 k5_gic_opt_shallow_copy(krb5_get_init_creds_opt *opt)
465 {
466 struct extended_options *opte;
467
468 opte = calloc(1, sizeof(*opte));
469 if (opt == NULL)
470 opte->opt.flags = DEFAULT_FLAGS;
471 else if (opt->flags & GIC_OPT_EXTENDED)
472 *opte = *(struct extended_options *)opt;
473 else
474 opte->opt = *opt;
475 opte->opt.flags |= GIC_OPT_SHALLOW_COPY;
476 return (krb5_get_init_creds_opt *)opte;
477 }
478
479 krb5_error_code KRB5_CALLCONV
krb5_get_init_creds_opt_set_pac_request(krb5_context context,krb5_get_init_creds_opt * opt,krb5_boolean req_pac)480 krb5_get_init_creds_opt_set_pac_request(krb5_context context,
481 krb5_get_init_creds_opt *opt,
482 krb5_boolean req_pac)
483 {
484 struct extended_options *opte = (struct extended_options *)opt;
485
486 if (opt == NULL || !(opt->flags & GIC_OPT_EXTENDED))
487 return EINVAL;
488 opte->pac_request = !!req_pac;
489 return 0;
490 }
491
492 int
k5_gic_opt_pac_request(krb5_get_init_creds_opt * opt)493 k5_gic_opt_pac_request(krb5_get_init_creds_opt *opt)
494 {
495 struct extended_options *opte = (struct extended_options *)opt;
496
497 if (opt == NULL || !(opt->flags & GIC_OPT_EXTENDED))
498 return -1;
499 return opte->pac_request;
500 }
501