xref: /freebsd/crypto/krb5/src/tests/unlockiter.c (revision 7f2fe78b9dd5f51c821d771b63d2e096f6fd49e9)
1 /* -*- mode: c; c-basic-offset: 4; indent-tabs-mode: nil -*- */
2 /* tests/unlockiter.c - test program for unlocked iteration */
3 /*
4  * Copyright (C) 2014 by the Massachusetts Institute of Technology.
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 /*
34  * Test unlocked KDB iteration.
35  */
36 
37 #include <sys/types.h>
38 #include <sys/select.h>
39 #include <sys/time.h>
40 #include <sys/wait.h>
41 
42 #include <errno.h>
43 #include <krb5.h>
44 #include <kadm5/admin.h>
45 #include <signal.h>
46 #include <stdlib.h>
47 #include <string.h>             /* Some platforms need memset() for FD_ZERO */
48 #include <unistd.h>
49 
50 struct cb_arg {
51     int inpipe;
52     int outpipe;
53     int timeout;
54     int done;
55 };
56 
57 /* Helper function for cb(): read a sync byte (with possible timeout), then
58  * write a sync byte. */
59 static int
syncpair_rw(const char * name,struct cb_arg * arg,char * cp,int timeout)60 syncpair_rw(const char *name, struct cb_arg *arg, char *cp, int timeout)
61 {
62     struct timeval tv;
63     fd_set rset;
64     int nfds;
65 
66     FD_ZERO(&rset);
67     FD_SET(arg->inpipe, &rset);
68     tv.tv_sec = timeout;
69     tv.tv_usec = 0;
70 
71     printf("cb: waiting for %s sync pair\n", name);
72     nfds = select(arg->inpipe + 1, &rset,
73                   NULL, NULL, (timeout == 0) ? NULL : &tv);
74     if (nfds < 0)
75         return -1;
76     if (nfds == 0) {
77         errno = ETIMEDOUT;
78         return -1;
79     }
80     if (read(arg->inpipe, cp, 1) < 0)
81         return -1;
82     printf("cb: writing %s sync pair\n", name);
83     if (write(arg->outpipe, cp, 1) < 0)
84         return -1;
85     return 0;
86 }
87 
88 /* On the first iteration only, receive and send sync bytes to the locking
89  * child to drive its locking activities. */
90 static krb5_error_code
cb(void * argin,krb5_db_entry * ent)91 cb(void *argin, krb5_db_entry *ent)
92 {
93     struct cb_arg *arg = argin;
94     char c = '\0';
95 
96     if (arg->done)
97         return 0;
98 
99     if (syncpair_rw("first", arg, &c, 0) < 0) {
100         com_err("cb", errno, "first sync pair");
101         return errno;
102     }
103     if (syncpair_rw("second", arg, &c, arg->timeout) < 0) {
104         com_err("cb", errno, "second sync pair");
105         return errno;
106     }
107     printf("cb: waiting for final sync byte\n");
108     if (read(arg->inpipe, &c, 1) < 0) {
109         com_err("cb", errno, "final sync byte");
110         return errno;
111     }
112     arg->done = 1;
113     return 0;
114 }
115 
116 /* Parent process: iterate over the KDB, using a callback that synchronizes
117  * with the locking child. */
118 static int
iterator(struct cb_arg * cb_arg,char ** db_args,pid_t child)119 iterator(struct cb_arg *cb_arg, char **db_args, pid_t child)
120 {
121     krb5_error_code retval;
122     krb5_context ctx;
123 
124     retval = krb5_init_context_profile(NULL, KRB5_INIT_CONTEXT_KDC, &ctx);
125     if (retval)
126         goto cleanup;
127 
128     retval = krb5_db_open(ctx, db_args,
129                           KRB5_KDB_OPEN_RW | KRB5_KDB_SRV_TYPE_ADMIN);
130     if (retval)
131         goto cleanup;
132 
133     retval = krb5_db_iterate(ctx, NULL, cb, cb_arg, 0);
134     if (retval)
135         goto cleanup;
136 
137     retval = krb5_db_fini(ctx);
138 
139 cleanup:
140     krb5_free_context(ctx);
141     if (retval) {
142         com_err("iterator", retval, "");
143         kill(child, SIGTERM);
144         exit(1);
145     }
146     return retval;
147 }
148 
149 /* Helper function for locker(): write, then receive a sync byte. */
150 static int
syncpair_wr(const char * name,int inpipe,int outpipe,unsigned char * cp)151 syncpair_wr(const char *name, int inpipe, int outpipe, unsigned char *cp)
152 {
153     printf("locker: writing %s sync pair\n", name);
154     if (write(outpipe, cp, 1) < 0)
155         return -1;
156     printf("locker: waiting for %s sync pair\n", name);
157     if (read(inpipe, cp, 1) < 0)
158         return -1;
159     return 0;
160 }
161 
162 /* Child process: acquire and release a write lock on the KDB, synchronized
163  * with parent. */
164 static int
locker(int inpipe,int outpipe,char ** db_args)165 locker(int inpipe, int outpipe, char **db_args)
166 {
167     krb5_error_code retval;
168     unsigned char c = '\0';
169     krb5_context ctx;
170 
171     retval = krb5_init_context_profile(NULL, KRB5_INIT_CONTEXT_KDC, &ctx);
172     if (retval)
173         goto cleanup;
174 
175     retval = krb5_db_open(ctx, db_args,
176                           KRB5_KDB_OPEN_RW | KRB5_KDB_SRV_TYPE_ADMIN);
177     if (retval)
178         goto cleanup;
179 
180     if (syncpair_wr("first", inpipe, outpipe, &c) < 0) {
181         retval = errno;
182         goto cleanup;
183     }
184     printf("locker: acquiring lock...\n");
185     retval = krb5_db_lock(ctx, KRB5_DB_LOCKMODE_EXCLUSIVE);
186     if (retval)
187         goto cleanup;
188     printf("locker: acquired lock\n");
189     if (syncpair_wr("second", inpipe, outpipe, &c) < 0) {
190         retval = errno;
191         goto cleanup;
192     }
193     krb5_db_unlock(ctx);
194     printf("locker: released lock\n");
195     printf("locker: writing final sync byte\n");
196     if (write(outpipe, &c, 1) < 0) {
197         retval = errno;
198         goto cleanup;
199     }
200     retval = krb5_db_fini(ctx);
201 cleanup:
202     if (retval)
203         com_err("locker", retval, "");
204 
205     krb5_free_context(ctx);
206     exit(retval != 0);
207 }
208 
209 static void
usage(const char * prog)210 usage(const char *prog)
211 {
212     fprintf(stderr, "usage: %s [-lu] [-t timeout]\n", prog);
213     exit(1);
214 }
215 
216 int
main(int argc,char * argv[])217 main(int argc, char *argv[])
218 {
219     struct cb_arg cb_arg;
220     pid_t child;
221     char *db_args[2] = { NULL, NULL };
222     int c;
223     int cstatus;
224     int pipe_to_locker[2], pipe_to_iterator[2];
225 
226     cb_arg.timeout = 1;
227     cb_arg.done = 0;
228     while ((c = getopt(argc, argv, "lt:u")) != -1) {
229         switch (c) {
230         case 'l':
231             db_args[0] = "lockiter";
232             break;
233         case 't':
234             cb_arg.timeout = atoi(optarg);
235             break;
236         case 'u':
237             db_args[0] = "unlockiter";
238             break;
239         default:
240             usage(argv[0]);
241         }
242     }
243     if (pipe(pipe_to_locker) < 0) {
244         com_err(argv[0], errno, "pipe(p_il)");
245         exit(1);
246     }
247     if (pipe(pipe_to_iterator) < 0) {
248         com_err(argv[0], errno, "pipe(p_li)");
249         exit(1);
250     }
251     cb_arg.inpipe = pipe_to_iterator[0];
252     cb_arg.outpipe = pipe_to_locker[1];
253     child = fork();
254     switch (child) {
255     case -1:
256         com_err(argv[0], errno, "fork");
257         exit(1);
258         break;
259     case 0:
260         locker(pipe_to_locker[0], pipe_to_iterator[1], db_args);
261         break;
262     default:
263         if (iterator(&cb_arg, db_args, child))
264             exit(1);
265         if (wait(&cstatus) < 0) {
266             com_err(argv[0], errno, "wait");
267             exit(1);
268         }
269         if (WIFSIGNALED(cstatus))
270             exit(1);
271         if (WIFEXITED(cstatus) && WEXITSTATUS(cstatus) != 0) {
272             exit(1);
273         }
274     }
275     exit(0);
276 }
277