xref: /freebsd/crypto/krb5/src/tests/unlockiter.c (revision 7f2fe78b9dd5f51c821d771b63d2e096f6fd49e9)
1*7f2fe78bSCy Schubert /* -*- mode: c; c-basic-offset: 4; indent-tabs-mode: nil -*- */
2*7f2fe78bSCy Schubert /* tests/unlockiter.c - test program for unlocked iteration */
3*7f2fe78bSCy Schubert /*
4*7f2fe78bSCy Schubert  * Copyright (C) 2014 by the Massachusetts Institute of Technology.
5*7f2fe78bSCy Schubert  * All rights reserved.
6*7f2fe78bSCy Schubert  *
7*7f2fe78bSCy Schubert  * Redistribution and use in source and binary forms, with or without
8*7f2fe78bSCy Schubert  * modification, are permitted provided that the following conditions
9*7f2fe78bSCy Schubert  * are met:
10*7f2fe78bSCy Schubert  *
11*7f2fe78bSCy Schubert  * * Redistributions of source code must retain the above copyright
12*7f2fe78bSCy Schubert  *   notice, this list of conditions and the following disclaimer.
13*7f2fe78bSCy Schubert  *
14*7f2fe78bSCy Schubert  * * Redistributions in binary form must reproduce the above copyright
15*7f2fe78bSCy Schubert  *   notice, this list of conditions and the following disclaimer in
16*7f2fe78bSCy Schubert  *   the documentation and/or other materials provided with the
17*7f2fe78bSCy Schubert  *   distribution.
18*7f2fe78bSCy Schubert  *
19*7f2fe78bSCy Schubert  * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS
20*7f2fe78bSCy Schubert  * "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT
21*7f2fe78bSCy Schubert  * LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS
22*7f2fe78bSCy Schubert  * FOR A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE
23*7f2fe78bSCy Schubert  * COPYRIGHT HOLDER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT,
24*7f2fe78bSCy Schubert  * INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES
25*7f2fe78bSCy Schubert  * (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR
26*7f2fe78bSCy Schubert  * SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION)
27*7f2fe78bSCy Schubert  * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT,
28*7f2fe78bSCy Schubert  * STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE)
29*7f2fe78bSCy Schubert  * ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED
30*7f2fe78bSCy Schubert  * OF THE POSSIBILITY OF SUCH DAMAGE.
31*7f2fe78bSCy Schubert  */
32*7f2fe78bSCy Schubert 
33*7f2fe78bSCy Schubert /*
34*7f2fe78bSCy Schubert  * Test unlocked KDB iteration.
35*7f2fe78bSCy Schubert  */
36*7f2fe78bSCy Schubert 
37*7f2fe78bSCy Schubert #include <sys/types.h>
38*7f2fe78bSCy Schubert #include <sys/select.h>
39*7f2fe78bSCy Schubert #include <sys/time.h>
40*7f2fe78bSCy Schubert #include <sys/wait.h>
41*7f2fe78bSCy Schubert 
42*7f2fe78bSCy Schubert #include <errno.h>
43*7f2fe78bSCy Schubert #include <krb5.h>
44*7f2fe78bSCy Schubert #include <kadm5/admin.h>
45*7f2fe78bSCy Schubert #include <signal.h>
46*7f2fe78bSCy Schubert #include <stdlib.h>
47*7f2fe78bSCy Schubert #include <string.h>             /* Some platforms need memset() for FD_ZERO */
48*7f2fe78bSCy Schubert #include <unistd.h>
49*7f2fe78bSCy Schubert 
50*7f2fe78bSCy Schubert struct cb_arg {
51*7f2fe78bSCy Schubert     int inpipe;
52*7f2fe78bSCy Schubert     int outpipe;
53*7f2fe78bSCy Schubert     int timeout;
54*7f2fe78bSCy Schubert     int done;
55*7f2fe78bSCy Schubert };
56*7f2fe78bSCy Schubert 
57*7f2fe78bSCy Schubert /* Helper function for cb(): read a sync byte (with possible timeout), then
58*7f2fe78bSCy Schubert  * write a sync byte. */
59*7f2fe78bSCy Schubert static int
syncpair_rw(const char * name,struct cb_arg * arg,char * cp,int timeout)60*7f2fe78bSCy Schubert syncpair_rw(const char *name, struct cb_arg *arg, char *cp, int timeout)
61*7f2fe78bSCy Schubert {
62*7f2fe78bSCy Schubert     struct timeval tv;
63*7f2fe78bSCy Schubert     fd_set rset;
64*7f2fe78bSCy Schubert     int nfds;
65*7f2fe78bSCy Schubert 
66*7f2fe78bSCy Schubert     FD_ZERO(&rset);
67*7f2fe78bSCy Schubert     FD_SET(arg->inpipe, &rset);
68*7f2fe78bSCy Schubert     tv.tv_sec = timeout;
69*7f2fe78bSCy Schubert     tv.tv_usec = 0;
70*7f2fe78bSCy Schubert 
71*7f2fe78bSCy Schubert     printf("cb: waiting for %s sync pair\n", name);
72*7f2fe78bSCy Schubert     nfds = select(arg->inpipe + 1, &rset,
73*7f2fe78bSCy Schubert                   NULL, NULL, (timeout == 0) ? NULL : &tv);
74*7f2fe78bSCy Schubert     if (nfds < 0)
75*7f2fe78bSCy Schubert         return -1;
76*7f2fe78bSCy Schubert     if (nfds == 0) {
77*7f2fe78bSCy Schubert         errno = ETIMEDOUT;
78*7f2fe78bSCy Schubert         return -1;
79*7f2fe78bSCy Schubert     }
80*7f2fe78bSCy Schubert     if (read(arg->inpipe, cp, 1) < 0)
81*7f2fe78bSCy Schubert         return -1;
82*7f2fe78bSCy Schubert     printf("cb: writing %s sync pair\n", name);
83*7f2fe78bSCy Schubert     if (write(arg->outpipe, cp, 1) < 0)
84*7f2fe78bSCy Schubert         return -1;
85*7f2fe78bSCy Schubert     return 0;
86*7f2fe78bSCy Schubert }
87*7f2fe78bSCy Schubert 
88*7f2fe78bSCy Schubert /* On the first iteration only, receive and send sync bytes to the locking
89*7f2fe78bSCy Schubert  * child to drive its locking activities. */
90*7f2fe78bSCy Schubert static krb5_error_code
cb(void * argin,krb5_db_entry * ent)91*7f2fe78bSCy Schubert cb(void *argin, krb5_db_entry *ent)
92*7f2fe78bSCy Schubert {
93*7f2fe78bSCy Schubert     struct cb_arg *arg = argin;
94*7f2fe78bSCy Schubert     char c = '\0';
95*7f2fe78bSCy Schubert 
96*7f2fe78bSCy Schubert     if (arg->done)
97*7f2fe78bSCy Schubert         return 0;
98*7f2fe78bSCy Schubert 
99*7f2fe78bSCy Schubert     if (syncpair_rw("first", arg, &c, 0) < 0) {
100*7f2fe78bSCy Schubert         com_err("cb", errno, "first sync pair");
101*7f2fe78bSCy Schubert         return errno;
102*7f2fe78bSCy Schubert     }
103*7f2fe78bSCy Schubert     if (syncpair_rw("second", arg, &c, arg->timeout) < 0) {
104*7f2fe78bSCy Schubert         com_err("cb", errno, "second sync pair");
105*7f2fe78bSCy Schubert         return errno;
106*7f2fe78bSCy Schubert     }
107*7f2fe78bSCy Schubert     printf("cb: waiting for final sync byte\n");
108*7f2fe78bSCy Schubert     if (read(arg->inpipe, &c, 1) < 0) {
109*7f2fe78bSCy Schubert         com_err("cb", errno, "final sync byte");
110*7f2fe78bSCy Schubert         return errno;
111*7f2fe78bSCy Schubert     }
112*7f2fe78bSCy Schubert     arg->done = 1;
113*7f2fe78bSCy Schubert     return 0;
114*7f2fe78bSCy Schubert }
115*7f2fe78bSCy Schubert 
116*7f2fe78bSCy Schubert /* Parent process: iterate over the KDB, using a callback that synchronizes
117*7f2fe78bSCy Schubert  * with the locking child. */
118*7f2fe78bSCy Schubert static int
iterator(struct cb_arg * cb_arg,char ** db_args,pid_t child)119*7f2fe78bSCy Schubert iterator(struct cb_arg *cb_arg, char **db_args, pid_t child)
120*7f2fe78bSCy Schubert {
121*7f2fe78bSCy Schubert     krb5_error_code retval;
122*7f2fe78bSCy Schubert     krb5_context ctx;
123*7f2fe78bSCy Schubert 
124*7f2fe78bSCy Schubert     retval = krb5_init_context_profile(NULL, KRB5_INIT_CONTEXT_KDC, &ctx);
125*7f2fe78bSCy Schubert     if (retval)
126*7f2fe78bSCy Schubert         goto cleanup;
127*7f2fe78bSCy Schubert 
128*7f2fe78bSCy Schubert     retval = krb5_db_open(ctx, db_args,
129*7f2fe78bSCy Schubert                           KRB5_KDB_OPEN_RW | KRB5_KDB_SRV_TYPE_ADMIN);
130*7f2fe78bSCy Schubert     if (retval)
131*7f2fe78bSCy Schubert         goto cleanup;
132*7f2fe78bSCy Schubert 
133*7f2fe78bSCy Schubert     retval = krb5_db_iterate(ctx, NULL, cb, cb_arg, 0);
134*7f2fe78bSCy Schubert     if (retval)
135*7f2fe78bSCy Schubert         goto cleanup;
136*7f2fe78bSCy Schubert 
137*7f2fe78bSCy Schubert     retval = krb5_db_fini(ctx);
138*7f2fe78bSCy Schubert 
139*7f2fe78bSCy Schubert cleanup:
140*7f2fe78bSCy Schubert     krb5_free_context(ctx);
141*7f2fe78bSCy Schubert     if (retval) {
142*7f2fe78bSCy Schubert         com_err("iterator", retval, "");
143*7f2fe78bSCy Schubert         kill(child, SIGTERM);
144*7f2fe78bSCy Schubert         exit(1);
145*7f2fe78bSCy Schubert     }
146*7f2fe78bSCy Schubert     return retval;
147*7f2fe78bSCy Schubert }
148*7f2fe78bSCy Schubert 
149*7f2fe78bSCy Schubert /* Helper function for locker(): write, then receive a sync byte. */
150*7f2fe78bSCy Schubert static int
syncpair_wr(const char * name,int inpipe,int outpipe,unsigned char * cp)151*7f2fe78bSCy Schubert syncpair_wr(const char *name, int inpipe, int outpipe, unsigned char *cp)
152*7f2fe78bSCy Schubert {
153*7f2fe78bSCy Schubert     printf("locker: writing %s sync pair\n", name);
154*7f2fe78bSCy Schubert     if (write(outpipe, cp, 1) < 0)
155*7f2fe78bSCy Schubert         return -1;
156*7f2fe78bSCy Schubert     printf("locker: waiting for %s sync pair\n", name);
157*7f2fe78bSCy Schubert     if (read(inpipe, cp, 1) < 0)
158*7f2fe78bSCy Schubert         return -1;
159*7f2fe78bSCy Schubert     return 0;
160*7f2fe78bSCy Schubert }
161*7f2fe78bSCy Schubert 
162*7f2fe78bSCy Schubert /* Child process: acquire and release a write lock on the KDB, synchronized
163*7f2fe78bSCy Schubert  * with parent. */
164*7f2fe78bSCy Schubert static int
locker(int inpipe,int outpipe,char ** db_args)165*7f2fe78bSCy Schubert locker(int inpipe, int outpipe, char **db_args)
166*7f2fe78bSCy Schubert {
167*7f2fe78bSCy Schubert     krb5_error_code retval;
168*7f2fe78bSCy Schubert     unsigned char c = '\0';
169*7f2fe78bSCy Schubert     krb5_context ctx;
170*7f2fe78bSCy Schubert 
171*7f2fe78bSCy Schubert     retval = krb5_init_context_profile(NULL, KRB5_INIT_CONTEXT_KDC, &ctx);
172*7f2fe78bSCy Schubert     if (retval)
173*7f2fe78bSCy Schubert         goto cleanup;
174*7f2fe78bSCy Schubert 
175*7f2fe78bSCy Schubert     retval = krb5_db_open(ctx, db_args,
176*7f2fe78bSCy Schubert                           KRB5_KDB_OPEN_RW | KRB5_KDB_SRV_TYPE_ADMIN);
177*7f2fe78bSCy Schubert     if (retval)
178*7f2fe78bSCy Schubert         goto cleanup;
179*7f2fe78bSCy Schubert 
180*7f2fe78bSCy Schubert     if (syncpair_wr("first", inpipe, outpipe, &c) < 0) {
181*7f2fe78bSCy Schubert         retval = errno;
182*7f2fe78bSCy Schubert         goto cleanup;
183*7f2fe78bSCy Schubert     }
184*7f2fe78bSCy Schubert     printf("locker: acquiring lock...\n");
185*7f2fe78bSCy Schubert     retval = krb5_db_lock(ctx, KRB5_DB_LOCKMODE_EXCLUSIVE);
186*7f2fe78bSCy Schubert     if (retval)
187*7f2fe78bSCy Schubert         goto cleanup;
188*7f2fe78bSCy Schubert     printf("locker: acquired lock\n");
189*7f2fe78bSCy Schubert     if (syncpair_wr("second", inpipe, outpipe, &c) < 0) {
190*7f2fe78bSCy Schubert         retval = errno;
191*7f2fe78bSCy Schubert         goto cleanup;
192*7f2fe78bSCy Schubert     }
193*7f2fe78bSCy Schubert     krb5_db_unlock(ctx);
194*7f2fe78bSCy Schubert     printf("locker: released lock\n");
195*7f2fe78bSCy Schubert     printf("locker: writing final sync byte\n");
196*7f2fe78bSCy Schubert     if (write(outpipe, &c, 1) < 0) {
197*7f2fe78bSCy Schubert         retval = errno;
198*7f2fe78bSCy Schubert         goto cleanup;
199*7f2fe78bSCy Schubert     }
200*7f2fe78bSCy Schubert     retval = krb5_db_fini(ctx);
201*7f2fe78bSCy Schubert cleanup:
202*7f2fe78bSCy Schubert     if (retval)
203*7f2fe78bSCy Schubert         com_err("locker", retval, "");
204*7f2fe78bSCy Schubert 
205*7f2fe78bSCy Schubert     krb5_free_context(ctx);
206*7f2fe78bSCy Schubert     exit(retval != 0);
207*7f2fe78bSCy Schubert }
208*7f2fe78bSCy Schubert 
209*7f2fe78bSCy Schubert static void
usage(const char * prog)210*7f2fe78bSCy Schubert usage(const char *prog)
211*7f2fe78bSCy Schubert {
212*7f2fe78bSCy Schubert     fprintf(stderr, "usage: %s [-lu] [-t timeout]\n", prog);
213*7f2fe78bSCy Schubert     exit(1);
214*7f2fe78bSCy Schubert }
215*7f2fe78bSCy Schubert 
216*7f2fe78bSCy Schubert int
main(int argc,char * argv[])217*7f2fe78bSCy Schubert main(int argc, char *argv[])
218*7f2fe78bSCy Schubert {
219*7f2fe78bSCy Schubert     struct cb_arg cb_arg;
220*7f2fe78bSCy Schubert     pid_t child;
221*7f2fe78bSCy Schubert     char *db_args[2] = { NULL, NULL };
222*7f2fe78bSCy Schubert     int c;
223*7f2fe78bSCy Schubert     int cstatus;
224*7f2fe78bSCy Schubert     int pipe_to_locker[2], pipe_to_iterator[2];
225*7f2fe78bSCy Schubert 
226*7f2fe78bSCy Schubert     cb_arg.timeout = 1;
227*7f2fe78bSCy Schubert     cb_arg.done = 0;
228*7f2fe78bSCy Schubert     while ((c = getopt(argc, argv, "lt:u")) != -1) {
229*7f2fe78bSCy Schubert         switch (c) {
230*7f2fe78bSCy Schubert         case 'l':
231*7f2fe78bSCy Schubert             db_args[0] = "lockiter";
232*7f2fe78bSCy Schubert             break;
233*7f2fe78bSCy Schubert         case 't':
234*7f2fe78bSCy Schubert             cb_arg.timeout = atoi(optarg);
235*7f2fe78bSCy Schubert             break;
236*7f2fe78bSCy Schubert         case 'u':
237*7f2fe78bSCy Schubert             db_args[0] = "unlockiter";
238*7f2fe78bSCy Schubert             break;
239*7f2fe78bSCy Schubert         default:
240*7f2fe78bSCy Schubert             usage(argv[0]);
241*7f2fe78bSCy Schubert         }
242*7f2fe78bSCy Schubert     }
243*7f2fe78bSCy Schubert     if (pipe(pipe_to_locker) < 0) {
244*7f2fe78bSCy Schubert         com_err(argv[0], errno, "pipe(p_il)");
245*7f2fe78bSCy Schubert         exit(1);
246*7f2fe78bSCy Schubert     }
247*7f2fe78bSCy Schubert     if (pipe(pipe_to_iterator) < 0) {
248*7f2fe78bSCy Schubert         com_err(argv[0], errno, "pipe(p_li)");
249*7f2fe78bSCy Schubert         exit(1);
250*7f2fe78bSCy Schubert     }
251*7f2fe78bSCy Schubert     cb_arg.inpipe = pipe_to_iterator[0];
252*7f2fe78bSCy Schubert     cb_arg.outpipe = pipe_to_locker[1];
253*7f2fe78bSCy Schubert     child = fork();
254*7f2fe78bSCy Schubert     switch (child) {
255*7f2fe78bSCy Schubert     case -1:
256*7f2fe78bSCy Schubert         com_err(argv[0], errno, "fork");
257*7f2fe78bSCy Schubert         exit(1);
258*7f2fe78bSCy Schubert         break;
259*7f2fe78bSCy Schubert     case 0:
260*7f2fe78bSCy Schubert         locker(pipe_to_locker[0], pipe_to_iterator[1], db_args);
261*7f2fe78bSCy Schubert         break;
262*7f2fe78bSCy Schubert     default:
263*7f2fe78bSCy Schubert         if (iterator(&cb_arg, db_args, child))
264*7f2fe78bSCy Schubert             exit(1);
265*7f2fe78bSCy Schubert         if (wait(&cstatus) < 0) {
266*7f2fe78bSCy Schubert             com_err(argv[0], errno, "wait");
267*7f2fe78bSCy Schubert             exit(1);
268*7f2fe78bSCy Schubert         }
269*7f2fe78bSCy Schubert         if (WIFSIGNALED(cstatus))
270*7f2fe78bSCy Schubert             exit(1);
271*7f2fe78bSCy Schubert         if (WIFEXITED(cstatus) && WEXITSTATUS(cstatus) != 0) {
272*7f2fe78bSCy Schubert             exit(1);
273*7f2fe78bSCy Schubert         }
274*7f2fe78bSCy Schubert     }
275*7f2fe78bSCy Schubert     exit(0);
276*7f2fe78bSCy Schubert }
277