xref: /freebsd/crypto/krb5/src/lib/krb5/krb/gic_opt.c (revision 7f2fe78b9dd5f51c821d771b63d2e096f6fd49e9)
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