1 /*
2 * This file contains the flask_op hypercall and associated functions.
3 *
4 * Author: George Coker, <gscoker@alpha.ncsc.mil>
5 *
6 * This program is free software; you can redistribute it and/or modify
7 * it under the terms of the GNU General Public License version 2,
8 * as published by the Free Software Foundation.
9 */
10 #ifndef COMPAT
11 #include <xen/errno.h>
12 #include <xen/event.h>
13 #include <xsm/xsm.h>
14 #include <xen/guest_access.h>
15 #include <xen/err.h>
16 #include <xen/param.h>
17
18 #include <public/xsm/flask_op.h>
19
20 #include <avc.h>
21 #include <avc_ss.h>
22 #include <objsec.h>
23 #include <conditional.h>
24
25 #define ret_t long
26 #define _copy_to_guest copy_to_guest
27 #define _copy_from_guest copy_from_guest
28
29 enum flask_bootparam_t __read_mostly flask_bootparam = FLASK_BOOTPARAM_ENFORCING;
30 static int parse_flask_param(const char *s);
31 custom_param("flask", parse_flask_param);
32
33 bool __read_mostly flask_enforcing = true;
34
35 #define MAX_POLICY_SIZE 0x4000000
36
37 #define FLASK_COPY_OUT \
38 ( \
39 1UL<<FLASK_CONTEXT_TO_SID | \
40 1UL<<FLASK_SID_TO_CONTEXT | \
41 1UL<<FLASK_ACCESS | \
42 1UL<<FLASK_CREATE | \
43 1UL<<FLASK_RELABEL | \
44 1UL<<FLASK_USER | \
45 1UL<<FLASK_GETBOOL | \
46 1UL<<FLASK_SETBOOL | \
47 1UL<<FLASK_AVC_HASHSTATS | \
48 1UL<<FLASK_AVC_CACHESTATS | \
49 1UL<<FLASK_MEMBER | \
50 1UL<<FLASK_GET_PEER_SID | \
51 0)
52
53 static DEFINE_SPINLOCK(sel_sem);
54
55 /* global data for booleans */
56 static int bool_num = 0;
57 static int *bool_pending_values = NULL;
58 static int flask_security_make_bools(void);
59
60 extern int ss_initialized;
61
parse_flask_param(const char * s)62 static int __init parse_flask_param(const char *s)
63 {
64 if ( !strcmp(s, "enforcing") )
65 flask_bootparam = FLASK_BOOTPARAM_ENFORCING;
66 else if ( !strcmp(s, "late") )
67 flask_bootparam = FLASK_BOOTPARAM_LATELOAD;
68 else if ( !strcmp(s, "disabled") )
69 flask_bootparam = FLASK_BOOTPARAM_DISABLED;
70 else if ( !strcmp(s, "permissive") )
71 flask_bootparam = FLASK_BOOTPARAM_PERMISSIVE;
72 else
73 flask_bootparam = FLASK_BOOTPARAM_INVALID;
74
75 return (flask_bootparam == FLASK_BOOTPARAM_INVALID) ? -EINVAL : 0;
76 }
77
domain_has_security(struct domain * d,u32 perms)78 static int domain_has_security(struct domain *d, u32 perms)
79 {
80 struct domain_security_struct *dsec;
81
82 dsec = d->ssid;
83 if ( !dsec )
84 return -EACCES;
85
86 return avc_has_perm(dsec->sid, SECINITSID_SECURITY, SECCLASS_SECURITY,
87 perms, NULL);
88 }
89
flask_security_relabel(struct xen_flask_transition * arg)90 static int flask_security_relabel(struct xen_flask_transition *arg)
91 {
92 int rv;
93
94 rv = domain_has_security(current->domain, SECURITY__COMPUTE_RELABEL);
95 if ( rv )
96 return rv;
97
98 rv = security_change_sid(arg->ssid, arg->tsid, arg->tclass, &arg->newsid);
99
100 return rv;
101 }
102
flask_security_create(struct xen_flask_transition * arg)103 static int flask_security_create(struct xen_flask_transition *arg)
104 {
105 int rv;
106
107 rv = domain_has_security(current->domain, SECURITY__COMPUTE_CREATE);
108 if ( rv )
109 return rv;
110
111 rv = security_transition_sid(arg->ssid, arg->tsid, arg->tclass, &arg->newsid);
112
113 return rv;
114 }
115
flask_security_access(struct xen_flask_access * arg)116 static int flask_security_access(struct xen_flask_access *arg)
117 {
118 struct av_decision avd;
119 int rv;
120
121 rv = domain_has_security(current->domain, SECURITY__COMPUTE_AV);
122 if ( rv )
123 return rv;
124
125 rv = security_compute_av(arg->ssid, arg->tsid, arg->tclass, arg->req, &avd);
126 if ( rv < 0 )
127 return rv;
128
129 arg->allowed = avd.allowed;
130 arg->audit_allow = avd.auditallow;
131 arg->audit_deny = avd.auditdeny;
132 arg->seqno = avd.seqno;
133
134 return rv;
135 }
136
flask_security_member(struct xen_flask_transition * arg)137 static int flask_security_member(struct xen_flask_transition *arg)
138 {
139 int rv;
140
141 rv = domain_has_security(current->domain, SECURITY__COMPUTE_MEMBER);
142 if ( rv )
143 return rv;
144
145 rv = security_member_sid(arg->ssid, arg->tsid, arg->tclass, &arg->newsid);
146
147 return rv;
148 }
149
flask_security_setenforce(struct xen_flask_setenforce * arg)150 static int flask_security_setenforce(struct xen_flask_setenforce *arg)
151 {
152 int enforce = !!(arg->enforcing);
153 int rv;
154
155 if ( enforce == flask_enforcing )
156 return 0;
157
158 rv = domain_has_security(current->domain, SECURITY__SETENFORCE);
159 if ( rv )
160 return rv;
161
162 flask_enforcing = enforce;
163
164 if ( flask_enforcing )
165 avc_ss_reset(0);
166
167 return 0;
168 }
169
170 #endif /* COMPAT */
171
flask_security_context(struct xen_flask_sid_context * arg)172 static int flask_security_context(struct xen_flask_sid_context *arg)
173 {
174 int rv;
175 char *buf;
176
177 rv = domain_has_security(current->domain, SECURITY__CHECK_CONTEXT);
178 if ( rv )
179 return rv;
180
181 buf = safe_copy_string_from_guest(arg->context, arg->size, PAGE_SIZE);
182 if ( IS_ERR(buf) )
183 return PTR_ERR(buf);
184
185 rv = security_context_to_sid(buf, arg->size, &arg->sid);
186 if ( rv < 0 )
187 goto out;
188
189 out:
190 xfree(buf);
191
192 return rv;
193 }
194
flask_security_sid(struct xen_flask_sid_context * arg)195 static int flask_security_sid(struct xen_flask_sid_context *arg)
196 {
197 int rv;
198 char *context;
199 u32 len;
200
201 rv = domain_has_security(current->domain, SECURITY__CHECK_CONTEXT);
202 if ( rv )
203 return rv;
204
205 rv = security_sid_to_context(arg->sid, &context, &len);
206 if ( rv < 0 )
207 return rv;
208
209 rv = 0;
210
211 if ( len > arg->size )
212 rv = -ERANGE;
213
214 arg->size = len;
215
216 if ( !rv && _copy_to_guest(arg->context, context, len) )
217 rv = -EFAULT;
218
219 xfree(context);
220
221 return rv;
222 }
223
224 #ifndef COMPAT
225
flask_disable(void)226 static int flask_disable(void)
227 {
228 static int flask_disabled = 0;
229
230 if ( ss_initialized )
231 {
232 /* Not permitted after initial policy load. */
233 return -EINVAL;
234 }
235
236 if ( flask_disabled )
237 {
238 /* Only do this once. */
239 return -EINVAL;
240 }
241
242 printk("Flask: Disabled at runtime.\n");
243
244 flask_disabled = 1;
245
246 /* Reset xsm_ops to the original module. */
247 xsm_ops = &dummy_xsm_ops;
248
249 return 0;
250 }
251
flask_security_setavc_threshold(struct xen_flask_setavc_threshold * arg)252 static int flask_security_setavc_threshold(struct xen_flask_setavc_threshold *arg)
253 {
254 int rv = 0;
255
256 if ( arg->threshold != avc_cache_threshold )
257 {
258 rv = domain_has_security(current->domain, SECURITY__SETSECPARAM);
259 if ( rv )
260 goto out;
261 avc_cache_threshold = arg->threshold;
262 }
263
264 out:
265 return rv;
266 }
267
268 #endif /* COMPAT */
269
flask_security_resolve_bool(struct xen_flask_boolean * arg)270 static int flask_security_resolve_bool(struct xen_flask_boolean *arg)
271 {
272 char *name;
273
274 if ( arg->bool_id != -1 )
275 return 0;
276
277 name = safe_copy_string_from_guest(arg->name, arg->size, PAGE_SIZE);
278 if ( IS_ERR(name) )
279 return PTR_ERR(name);
280
281 arg->bool_id = security_find_bool(name);
282 arg->size = 0;
283
284 xfree(name);
285
286 return 0;
287 }
288
flask_security_set_bool(struct xen_flask_boolean * arg)289 static int flask_security_set_bool(struct xen_flask_boolean *arg)
290 {
291 int rv;
292
293 rv = domain_has_security(current->domain, SECURITY__SETBOOL);
294 if ( rv )
295 return rv;
296
297 rv = flask_security_resolve_bool(arg);
298 if ( rv )
299 return rv;
300
301 spin_lock(&sel_sem);
302
303 if ( arg->commit )
304 {
305 int num;
306 int *values;
307
308 rv = security_get_bools(&num, NULL, &values, NULL);
309 if ( rv != 0 )
310 goto out;
311
312 if ( arg->bool_id >= num )
313 {
314 xfree(values);
315 rv = -ENOENT;
316 goto out;
317 }
318 values[arg->bool_id] = !!(arg->new_value);
319
320 arg->enforcing = arg->pending = !!(arg->new_value);
321
322 if ( bool_pending_values )
323 bool_pending_values[arg->bool_id] = !!(arg->new_value);
324
325 rv = security_set_bools(num, values);
326 xfree(values);
327 }
328 else
329 {
330 if ( !bool_pending_values )
331 rv = flask_security_make_bools();
332 if ( !rv && arg->bool_id >= bool_num )
333 rv = -ENOENT;
334 if ( rv )
335 goto out;
336
337 bool_pending_values[arg->bool_id] = !!(arg->new_value);
338 arg->pending = !!(arg->new_value);
339 arg->enforcing = security_get_bool_value(arg->bool_id);
340
341 rv = 0;
342 }
343
344 out:
345 spin_unlock(&sel_sem);
346 return rv;
347 }
348
flask_security_get_bool(struct xen_flask_boolean * arg)349 static int flask_security_get_bool(struct xen_flask_boolean *arg)
350 {
351 int rv;
352
353 rv = flask_security_resolve_bool(arg);
354 if ( rv )
355 return rv;
356
357 spin_lock(&sel_sem);
358
359 rv = security_get_bool_value(arg->bool_id);
360 if ( rv < 0 )
361 goto out;
362
363 arg->enforcing = rv;
364
365 if ( bool_pending_values )
366 arg->pending = bool_pending_values[arg->bool_id];
367 else
368 arg->pending = rv;
369
370 rv = 0;
371
372 if ( arg->size )
373 {
374 char *nameout = security_get_bool_name(arg->bool_id);
375 size_t nameout_len = strlen(nameout);
376 if ( nameout_len > arg->size )
377 rv = -ERANGE;
378 arg->size = nameout_len;
379
380 if ( !rv && _copy_to_guest(arg->name, nameout, nameout_len) )
381 rv = -EFAULT;
382 xfree(nameout);
383 }
384
385 out:
386 spin_unlock(&sel_sem);
387 return rv;
388 }
389
390 #ifndef COMPAT
391
flask_security_commit_bools(void)392 static int flask_security_commit_bools(void)
393 {
394 int rv;
395
396 spin_lock(&sel_sem);
397
398 rv = domain_has_security(current->domain, SECURITY__SETBOOL);
399 if ( rv )
400 goto out;
401
402 if ( bool_pending_values )
403 rv = security_set_bools(bool_num, bool_pending_values);
404
405 out:
406 spin_unlock(&sel_sem);
407 return rv;
408 }
409
flask_security_make_bools(void)410 static int flask_security_make_bools(void)
411 {
412 int ret = 0;
413 int num;
414 int *values = NULL;
415
416 xfree(bool_pending_values);
417
418 ret = security_get_bools(&num, NULL, &values, NULL);
419 if ( ret != 0 )
420 goto out;
421
422 bool_num = num;
423 bool_pending_values = values;
424
425 out:
426 return ret;
427 }
428
429 #ifdef CONFIG_XSM_FLASK_AVC_STATS
430
flask_security_avc_cachestats(struct xen_flask_cache_stats * arg)431 static int flask_security_avc_cachestats(struct xen_flask_cache_stats *arg)
432 {
433 struct avc_cache_stats *st;
434
435 if ( arg->cpu >= nr_cpu_ids )
436 return -ENOENT;
437 if ( !cpu_online(arg->cpu) )
438 return -ENOENT;
439
440 st = &per_cpu(avc_cache_stats, arg->cpu);
441
442 arg->lookups = st->lookups;
443 arg->hits = st->hits;
444 arg->misses = st->misses;
445 arg->allocations = st->allocations;
446 arg->reclaims = st->reclaims;
447 arg->frees = st->frees;
448
449 return 0;
450 }
451
452 #endif
453 #endif /* COMPAT */
454
flask_security_load(struct xen_flask_load * load)455 static int flask_security_load(struct xen_flask_load *load)
456 {
457 int ret;
458 void *buf = NULL;
459 bool is_reload = ss_initialized;
460
461 ret = domain_has_security(current->domain, SECURITY__LOAD_POLICY);
462 if ( ret )
463 return ret;
464
465 if ( load->size > MAX_POLICY_SIZE )
466 return -EINVAL;
467
468 buf = xmalloc_bytes(load->size);
469 if ( !buf )
470 return -ENOMEM;
471
472 if ( _copy_from_guest(buf, load->buffer, load->size) )
473 {
474 ret = -EFAULT;
475 goto out_free;
476 }
477
478 spin_lock(&sel_sem);
479
480 ret = security_load_policy(buf, load->size);
481 if ( ret )
482 goto out;
483
484 if ( !is_reload )
485 printk(XENLOG_INFO "Flask: Policy loaded, continuing in %s mode.\n",
486 flask_enforcing ? "enforcing" : "permissive");
487
488 xfree(bool_pending_values);
489 bool_pending_values = NULL;
490 ret = 0;
491
492 out:
493 spin_unlock(&sel_sem);
494 out_free:
495 xfree(buf);
496 return ret;
497 }
498
flask_devicetree_label(struct xen_flask_devicetree_label * arg)499 static int flask_devicetree_label(struct xen_flask_devicetree_label *arg)
500 {
501 int rv;
502 char *buf;
503 u32 sid = arg->sid;
504 u32 perm = sid ? SECURITY__ADD_OCONTEXT : SECURITY__DEL_OCONTEXT;
505
506 rv = domain_has_security(current->domain, perm);
507 if ( rv )
508 return rv;
509
510 buf = safe_copy_string_from_guest(arg->path, arg->length, PAGE_SIZE);
511 if ( IS_ERR(buf) )
512 return PTR_ERR(buf);
513
514 /* buf is consumed or freed by this function */
515 rv = security_devicetree_setlabel(buf, sid);
516
517 return rv;
518 }
519
520 #ifndef COMPAT
521
flask_ocontext_del(struct xen_flask_ocontext * arg)522 static int flask_ocontext_del(struct xen_flask_ocontext *arg)
523 {
524 int rv;
525
526 if ( arg->low > arg->high )
527 return -EINVAL;
528
529 rv = domain_has_security(current->domain, SECURITY__DEL_OCONTEXT);
530 if ( rv )
531 return rv;
532
533 return security_ocontext_del(arg->ocon, arg->low, arg->high);
534 }
535
flask_ocontext_add(struct xen_flask_ocontext * arg)536 static int flask_ocontext_add(struct xen_flask_ocontext *arg)
537 {
538 int rv;
539
540 if ( arg->low > arg->high )
541 return -EINVAL;
542
543 rv = domain_has_security(current->domain, SECURITY__ADD_OCONTEXT);
544 if ( rv )
545 return rv;
546
547 return security_ocontext_add(arg->ocon, arg->low, arg->high, arg->sid);
548 }
549
flask_get_peer_sid(struct xen_flask_peersid * arg)550 static int flask_get_peer_sid(struct xen_flask_peersid *arg)
551 {
552 int rv = -EINVAL;
553 struct domain *d = current->domain;
554 struct domain *peer;
555 struct evtchn *chn;
556 struct domain_security_struct *dsec;
557
558 spin_lock(&d->event_lock);
559
560 if ( !port_is_valid(d, arg->evtchn) )
561 goto out;
562
563 chn = evtchn_from_port(d, arg->evtchn);
564 if ( chn->state != ECS_INTERDOMAIN )
565 goto out;
566
567 peer = chn->u.interdomain.remote_dom;
568 if ( !peer )
569 goto out;
570
571 dsec = peer->ssid;
572 arg->sid = dsec->sid;
573 rv = 0;
574
575 out:
576 spin_unlock(&d->event_lock);
577 return rv;
578 }
579
flask_relabel_domain(struct xen_flask_relabel * arg)580 static int flask_relabel_domain(struct xen_flask_relabel *arg)
581 {
582 int rc;
583 struct domain *d;
584 struct domain_security_struct *csec = current->domain->ssid;
585 struct domain_security_struct *dsec;
586 struct avc_audit_data ad;
587 AVC_AUDIT_DATA_INIT(&ad, NONE);
588
589 d = rcu_lock_domain_by_any_id(arg->domid);
590 if ( d == NULL )
591 return -ESRCH;
592
593 ad.sdom = current->domain;
594 ad.tdom = d;
595 dsec = d->ssid;
596
597 if ( arg->domid == DOMID_SELF )
598 {
599 rc = avc_has_perm(dsec->sid, arg->sid, SECCLASS_DOMAIN2, DOMAIN2__RELABELSELF, &ad);
600 if ( rc )
601 goto out;
602 }
603 else
604 {
605 rc = avc_has_perm(csec->sid, dsec->sid, SECCLASS_DOMAIN2, DOMAIN2__RELABELFROM, &ad);
606 if ( rc )
607 goto out;
608
609 rc = avc_has_perm(csec->sid, arg->sid, SECCLASS_DOMAIN2, DOMAIN2__RELABELTO, &ad);
610 if ( rc )
611 goto out;
612 }
613
614 rc = avc_has_perm(dsec->sid, arg->sid, SECCLASS_DOMAIN, DOMAIN__TRANSITION, &ad);
615 if ( rc )
616 goto out;
617
618 dsec->sid = arg->sid;
619 dsec->self_sid = arg->sid;
620 security_transition_sid(dsec->sid, dsec->sid, SECCLASS_DOMAIN,
621 &dsec->self_sid);
622 if ( d->target )
623 {
624 struct domain_security_struct *tsec = d->target->ssid;
625 security_transition_sid(tsec->sid, dsec->sid, SECCLASS_DOMAIN,
626 &dsec->target_sid);
627 }
628
629 out:
630 rcu_unlock_domain(d);
631 return rc;
632 }
633
634 #endif /* !COMPAT */
635
do_flask_op(XEN_GUEST_HANDLE_PARAM (xsm_op_t)u_flask_op)636 ret_t do_flask_op(XEN_GUEST_HANDLE_PARAM(xsm_op_t) u_flask_op)
637 {
638 xen_flask_op_t op;
639 int rv;
640
641 if ( copy_from_guest(&op, u_flask_op, 1) )
642 return -EFAULT;
643
644 if ( op.interface_version != XEN_FLASK_INTERFACE_VERSION )
645 return -ENOSYS;
646
647 switch ( op.cmd )
648 {
649 case FLASK_LOAD:
650 rv = flask_security_load(&op.u.load);
651 break;
652
653 case FLASK_GETENFORCE:
654 rv = flask_enforcing;
655 break;
656
657 case FLASK_SETENFORCE:
658 rv = flask_security_setenforce(&op.u.enforce);
659 break;
660
661 case FLASK_CONTEXT_TO_SID:
662 rv = flask_security_context(&op.u.sid_context);
663 break;
664
665 case FLASK_SID_TO_CONTEXT:
666 rv = flask_security_sid(&op.u.sid_context);
667 break;
668
669 case FLASK_ACCESS:
670 rv = flask_security_access(&op.u.access);
671 break;
672
673 case FLASK_CREATE:
674 rv = flask_security_create(&op.u.transition);
675 break;
676
677 case FLASK_RELABEL:
678 rv = flask_security_relabel(&op.u.transition);
679 break;
680
681 case FLASK_POLICYVERS:
682 rv = POLICYDB_VERSION_MAX;
683 break;
684
685 case FLASK_GETBOOL:
686 rv = flask_security_get_bool(&op.u.boolean);
687 break;
688
689 case FLASK_SETBOOL:
690 rv = flask_security_set_bool(&op.u.boolean);
691 break;
692
693 case FLASK_COMMITBOOLS:
694 rv = flask_security_commit_bools();
695 break;
696
697 case FLASK_MLS:
698 rv = flask_mls_enabled;
699 break;
700
701 case FLASK_DISABLE:
702 rv = flask_disable();
703 break;
704
705 case FLASK_GETAVC_THRESHOLD:
706 rv = avc_cache_threshold;
707 break;
708
709 case FLASK_SETAVC_THRESHOLD:
710 rv = flask_security_setavc_threshold(&op.u.setavc_threshold);
711 break;
712
713 case FLASK_AVC_HASHSTATS:
714 rv = avc_get_hash_stats(&op.u.hash_stats);
715 break;
716
717 #ifdef CONFIG_XSM_FLASK_AVC_STATS
718 case FLASK_AVC_CACHESTATS:
719 rv = flask_security_avc_cachestats(&op.u.cache_stats);
720 break;
721 #endif
722
723 case FLASK_MEMBER:
724 rv = flask_security_member(&op.u.transition);
725 break;
726
727 case FLASK_ADD_OCONTEXT:
728 rv = flask_ocontext_add(&op.u.ocontext);
729 break;
730
731 case FLASK_DEL_OCONTEXT:
732 rv = flask_ocontext_del(&op.u.ocontext);
733 break;
734
735 case FLASK_GET_PEER_SID:
736 rv = flask_get_peer_sid(&op.u.peersid);
737 break;
738
739 case FLASK_RELABEL_DOMAIN:
740 rv = flask_relabel_domain(&op.u.relabel);
741 break;
742
743 case FLASK_DEVICETREE_LABEL:
744 rv = flask_devicetree_label(&op.u.devicetree_label);
745 break;
746
747 default:
748 rv = -ENOSYS;
749 }
750
751 if ( rv < 0 )
752 goto out;
753
754 if ( (FLASK_COPY_OUT&(1UL<<op.cmd)) )
755 {
756 if ( copy_to_guest(u_flask_op, &op, 1) )
757 rv = -EFAULT;
758 }
759
760 out:
761 return rv;
762 }
763
764 #if defined(CONFIG_COMPAT) && !defined(COMPAT)
765 #undef _copy_to_guest
766 #define _copy_to_guest copy_to_compat
767 #undef _copy_from_guest
768 #define _copy_from_guest copy_from_compat
769
770 #include <compat/event_channel.h>
771 #include <compat/xsm/flask_op.h>
772
773 CHECK_flask_access;
774 CHECK_flask_cache_stats;
775 CHECK_flask_hash_stats;
776 CHECK_flask_ocontext;
777 CHECK_flask_peersid;
778 CHECK_flask_relabel;
779 CHECK_flask_setavc_threshold;
780 CHECK_flask_setenforce;
781 CHECK_flask_transition;
782
783 #define COMPAT
784 #define safe_copy_string_from_guest(ch, sz, mx) ({ \
785 XEN_GUEST_HANDLE_PARAM(char) gh; \
786 guest_from_compat_handle(gh, ch); \
787 safe_copy_string_from_guest(gh, sz, mx); \
788 })
789
790 #define xen_flask_load compat_flask_load
791 #define flask_security_load compat_security_load
792
793 #define xen_flask_userlist compat_flask_userlist
794
795 #define xen_flask_sid_context compat_flask_sid_context
796 #define flask_security_context compat_security_context
797 #define flask_security_sid compat_security_sid
798
799 #define xen_flask_boolean compat_flask_boolean
800 #define flask_security_resolve_bool compat_security_resolve_bool
801 #define flask_security_get_bool compat_security_get_bool
802 #define flask_security_set_bool compat_security_set_bool
803
804 #define xen_flask_devicetree_label compat_flask_devicetree_label
805 #define flask_devicetree_label compat_devicetree_label
806
807 #define xen_flask_op_t compat_flask_op_t
808 #undef ret_t
809 #define ret_t int
810 #define do_flask_op compat_flask_op
811
812 #include "flask_op.c"
813 #endif
814