log.c revision 1.1.1.2.10.1 1 1.1.1.2.10.1 bouyer /* $NetBSD: log.c,v 1.1.1.2.10.1 2017/04/21 16:50:49 bouyer Exp $ */
2 1.1 elric
3 1.1 elric /*
4 1.1 elric * Copyright (c) 1997 - 2007 Kungliga Tekniska Hgskolan
5 1.1 elric * (Royal Institute of Technology, Stockholm, Sweden).
6 1.1 elric * All rights reserved.
7 1.1 elric *
8 1.1 elric * Redistribution and use in source and binary forms, with or without
9 1.1 elric * modification, are permitted provided that the following conditions
10 1.1 elric * are met:
11 1.1 elric *
12 1.1 elric * 1. Redistributions of source code must retain the above copyright
13 1.1 elric * notice, this list of conditions and the following disclaimer.
14 1.1 elric *
15 1.1 elric * 2. Redistributions in binary form must reproduce the above copyright
16 1.1 elric * notice, this list of conditions and the following disclaimer in the
17 1.1 elric * documentation and/or other materials provided with the distribution.
18 1.1 elric *
19 1.1 elric * 3. Neither the name of the Institute nor the names of its contributors
20 1.1 elric * may be used to endorse or promote products derived from this software
21 1.1 elric * without specific prior written permission.
22 1.1 elric *
23 1.1 elric * THIS SOFTWARE IS PROVIDED BY THE INSTITUTE AND CONTRIBUTORS ``AS IS'' AND
24 1.1 elric * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
25 1.1 elric * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
26 1.1 elric * ARE DISCLAIMED. IN NO EVENT SHALL THE INSTITUTE OR CONTRIBUTORS BE LIABLE
27 1.1 elric * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
28 1.1 elric * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS
29 1.1 elric * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION)
30 1.1 elric * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT
31 1.1 elric * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY
32 1.1 elric * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF
33 1.1 elric * SUCH DAMAGE.
34 1.1 elric */
35 1.1 elric
36 1.1 elric #include "kadm5_locl.h"
37 1.1 elric #include "heim_threads.h"
38 1.1 elric
39 1.1.1.2.10.1 bouyer __RCSID("$NetBSD: log.c,v 1.1.1.2.10.1 2017/04/21 16:50:49 bouyer Exp $");
40 1.1 elric
41 1.1 elric /*
42 1.1.1.2.10.1 bouyer * A log consists of a sequence of records of this form:
43 1.1 elric *
44 1.1.1.2.10.1 bouyer * version number 4 bytes -\
45 1.1.1.2.10.1 bouyer * time in seconds 4 bytes +> preamble --+> header
46 1.1.1.2.10.1 bouyer * operation (enum kadm_ops) 4 bytes -/ /
47 1.1.1.2.10.1 bouyer * n, length of payload 4 bytes --------------+
48 1.1.1.2.10.1 bouyer * PAYLOAD DATA... n bytes
49 1.1.1.2.10.1 bouyer * n, length of payload 4 bytes ----------------+> trailer
50 1.1.1.2.10.1 bouyer * version number 4 bytes ->postamble ---/
51 1.1 elric *
52 1.1.1.2.10.1 bouyer * I.e., records have a header and a trailer so that knowing the offset
53 1.1.1.2.10.1 bouyer * of an record's start or end one can traverse the log forwards and
54 1.1.1.2.10.1 bouyer * backwards.
55 1.1.1.2.10.1 bouyer *
56 1.1.1.2.10.1 bouyer * The log always starts with a nop record (uber record) that contains the
57 1.1.1.2.10.1 bouyer * offset (8 bytes) of the first unconfirmed record (typically EOF), and the
58 1.1.1.2.10.1 bouyer * version number and timestamp of the preceding last confirmed record:
59 1.1.1.2.10.1 bouyer *
60 1.1.1.2.10.1 bouyer * offset of next new record 8 bytes
61 1.1.1.2.10.1 bouyer * last record time 4 bytes
62 1.1.1.2.10.1 bouyer * last record version number 4 bytes
63 1.1.1.2.10.1 bouyer *
64 1.1.1.2.10.1 bouyer * When an iprop slave receives a complete database, it saves that version as
65 1.1.1.2.10.1 bouyer * the last confirmed version, without writing any other records to the log. We
66 1.1.1.2.10.1 bouyer * use that version as the basis for further updates.
67 1.1.1.2.10.1 bouyer *
68 1.1.1.2.10.1 bouyer * kadm5 write operations are done in this order:
69 1.1.1.2.10.1 bouyer *
70 1.1.1.2.10.1 bouyer * - replay unconfirmed log records
71 1.1.1.2.10.1 bouyer * - write (append) and fsync() the log record for the kadm5 update
72 1.1.1.2.10.1 bouyer * - update the HDB (which includes fsync() or moral equivalent)
73 1.1.1.2.10.1 bouyer * - update the log uber record to mark the log record written as
74 1.1.1.2.10.1 bouyer * confirmed (not fsync()ed)
75 1.1.1.2.10.1 bouyer *
76 1.1.1.2.10.1 bouyer * This makes it possible and safe to seek to the logical end of the log
77 1.1.1.2.10.1 bouyer * (that is, the end of the last confirmed record) without traversing
78 1.1.1.2.10.1 bouyer * the whole log forward from offset zero. Unconfirmed records (which
79 1.1.1.2.10.1 bouyer * -currently- should never be more than one) can then be found (and
80 1.1.1.2.10.1 bouyer * rolled forward) by traversing forward from the logical end of the
81 1.1.1.2.10.1 bouyer * log. The trailers make it possible to traverse the log backwards
82 1.1.1.2.10.1 bouyer * from the logical end.
83 1.1.1.2.10.1 bouyer *
84 1.1.1.2.10.1 bouyer * This also makes the log + the HDB a two-phase commit with
85 1.1.1.2.10.1 bouyer * roll-forward system.
86 1.1.1.2.10.1 bouyer *
87 1.1.1.2.10.1 bouyer * HDB entry exists and HDB entry does not exist errors occurring during
88 1.1.1.2.10.1 bouyer * replay of unconfirmed records are ignored. This is because the
89 1.1.1.2.10.1 bouyer * corresponding HDB update might have completed. But also because a
90 1.1.1.2.10.1 bouyer * change to add aliases to a principal can fail because we don't check
91 1.1.1.2.10.1 bouyer * for alias conflicts before going ahead with the write operation.
92 1.1.1.2.10.1 bouyer *
93 1.1.1.2.10.1 bouyer * Non-sensical and incomplete log records found during roll-forward are
94 1.1.1.2.10.1 bouyer * truncated. A log record is non-sensical if its header and trailer
95 1.1.1.2.10.1 bouyer * don't match.
96 1.1.1.2.10.1 bouyer *
97 1.1.1.2.10.1 bouyer * Recovery (by rolling forward) occurs at the next read or write by a
98 1.1.1.2.10.1 bouyer * kadm5 API reader (e.g., kadmin), but not by an hdb API reader (e.g.,
99 1.1.1.2.10.1 bouyer * the KDC). This means that, e.g., a principal rename could fail in
100 1.1.1.2.10.1 bouyer * between the store and the delete, and recovery might not take place
101 1.1.1.2.10.1 bouyer * until the next write operation.
102 1.1.1.2.10.1 bouyer *
103 1.1.1.2.10.1 bouyer * The log record payload format for create is:
104 1.1.1.2.10.1 bouyer *
105 1.1.1.2.10.1 bouyer * DER-encoded HDB_entry n bytes
106 1.1.1.2.10.1 bouyer *
107 1.1.1.2.10.1 bouyer * The log record payload format for update is:
108 1.1.1.2.10.1 bouyer *
109 1.1.1.2.10.1 bouyer * mask 4 bytes
110 1.1.1.2.10.1 bouyer * DER-encoded HDB_entry n-4 bytes
111 1.1.1.2.10.1 bouyer *
112 1.1.1.2.10.1 bouyer * The log record payload format for delete is:
113 1.1.1.2.10.1 bouyer *
114 1.1.1.2.10.1 bouyer * krb5_store_principal n bytes
115 1.1.1.2.10.1 bouyer *
116 1.1.1.2.10.1 bouyer * The log record payload format for rename is:
117 1.1.1.2.10.1 bouyer *
118 1.1.1.2.10.1 bouyer * krb5_store_principal m bytes (old principal name)
119 1.1.1.2.10.1 bouyer * DER-encoded HDB_entry n-m bytes (new record)
120 1.1.1.2.10.1 bouyer *
121 1.1.1.2.10.1 bouyer * The log record payload format for nop varies:
122 1.1.1.2.10.1 bouyer *
123 1.1.1.2.10.1 bouyer * - The zeroth record in new logs is a nop with a 16 byte payload:
124 1.1.1.2.10.1 bouyer *
125 1.1.1.2.10.1 bouyer * offset of end of last confirmed record 8 bytes
126 1.1.1.2.10.1 bouyer * timestamp of last confirmed record 4 bytes
127 1.1.1.2.10.1 bouyer * version number of last confirmed record 4 bytes
128 1.1.1.2.10.1 bouyer *
129 1.1.1.2.10.1 bouyer * - New non-zeroth nop records:
130 1.1.1.2.10.1 bouyer *
131 1.1.1.2.10.1 bouyer * nop type 4 bytes
132 1.1.1.2.10.1 bouyer *
133 1.1.1.2.10.1 bouyer * - Old nop records:
134 1.1.1.2.10.1 bouyer *
135 1.1.1.2.10.1 bouyer * version number 4 bytes
136 1.1.1.2.10.1 bouyer * timestamp 4 bytes
137 1.1.1.2.10.1 bouyer *
138 1.1.1.2.10.1 bouyer * Upon initialization, the log's uber record will have version 1, and
139 1.1.1.2.10.1 bouyer * will be followed by a nop record with version 2. The version numbers
140 1.1.1.2.10.1 bouyer * of additional records will be monotonically increasing.
141 1.1.1.2.10.1 bouyer *
142 1.1.1.2.10.1 bouyer * Truncation (kadm5_log_truncate()) takes some N > 0 records from the
143 1.1.1.2.10.1 bouyer * tail of the log and writes them to the beginning of the log after an
144 1.1.1.2.10.1 bouyer * uber record whose version will then be one less than the first of
145 1.1.1.2.10.1 bouyer * those records.
146 1.1.1.2.10.1 bouyer *
147 1.1.1.2.10.1 bouyer * On masters the log should never have more than one unconfirmed
148 1.1.1.2.10.1 bouyer * record, but slaves append all of a master's "diffs" and then call
149 1.1.1.2.10.1 bouyer * kadm5_log_recover() to recover.
150 1.1.1.2.10.1 bouyer */
151 1.1.1.2.10.1 bouyer
152 1.1.1.2.10.1 bouyer /*
153 1.1.1.2.10.1 bouyer * HDB and log lock order on the master:
154 1.1.1.2.10.1 bouyer *
155 1.1.1.2.10.1 bouyer * 1) open and lock the HDB
156 1.1.1.2.10.1 bouyer * 2) open and lock the log
157 1.1.1.2.10.1 bouyer * 3) do something
158 1.1.1.2.10.1 bouyer * 4) unlock and close the log
159 1.1.1.2.10.1 bouyer * 5) repeat (2)..(4) if desired
160 1.1.1.2.10.1 bouyer * 6) unlock and close the HDB
161 1.1.1.2.10.1 bouyer *
162 1.1.1.2.10.1 bouyer * The kadmin -l lock command can be used to hold the HDB open and
163 1.1.1.2.10.1 bouyer * locked for multiple operations.
164 1.1.1.2.10.1 bouyer *
165 1.1.1.2.10.1 bouyer * HDB and log lock order on the slave:
166 1.1.1.2.10.1 bouyer *
167 1.1.1.2.10.1 bouyer * 1) open and lock the log
168 1.1.1.2.10.1 bouyer * 2) open and lock the HDB
169 1.1.1.2.10.1 bouyer * 3) replay entries
170 1.1.1.2.10.1 bouyer * 4) unlock and close the HDB
171 1.1.1.2.10.1 bouyer * 5) repeat (2)..(4) until signaled
172 1.1.1.2.10.1 bouyer * 6) unlock and close the HDB
173 1.1.1.2.10.1 bouyer *
174 1.1.1.2.10.1 bouyer * The slave doesn't want to allow other local writers, after all, thus
175 1.1.1.2.10.1 bouyer * the order is reversed. This means that using "kadmin -l" on a slave
176 1.1.1.2.10.1 bouyer * will deadlock with ipropd-slave -- don't do that.
177 1.1.1.2.10.1 bouyer */
178 1.1.1.2.10.1 bouyer
179 1.1.1.2.10.1 bouyer #define LOG_HEADER_SZ ((off_t)(sizeof(uint32_t) * 4))
180 1.1.1.2.10.1 bouyer #define LOG_TRAILER_SZ ((off_t)(sizeof(uint32_t) * 2))
181 1.1.1.2.10.1 bouyer #define LOG_WRAPPER_SZ ((off_t)(LOG_HEADER_SZ + LOG_TRAILER_SZ))
182 1.1.1.2.10.1 bouyer #define LOG_UBER_LEN ((off_t)(sizeof(uint64_t) + sizeof(uint32_t) * 2))
183 1.1.1.2.10.1 bouyer #define LOG_UBER_SZ ((off_t)(LOG_WRAPPER_SZ + LOG_UBER_LEN))
184 1.1.1.2.10.1 bouyer
185 1.1.1.2.10.1 bouyer #define LOG_NOPEEK 0
186 1.1.1.2.10.1 bouyer #define LOG_DOPEEK 1
187 1.1.1.2.10.1 bouyer
188 1.1.1.2.10.1 bouyer /*
189 1.1.1.2.10.1 bouyer * Read the header of the record starting at the current offset into sp.
190 1.1.1.2.10.1 bouyer *
191 1.1.1.2.10.1 bouyer * Preserves sp's offset on success if `peek', else skips the header.
192 1.1.1.2.10.1 bouyer *
193 1.1.1.2.10.1 bouyer * Preserves sp's offset on failure where possible.
194 1.1.1.2.10.1 bouyer */
195 1.1.1.2.10.1 bouyer static kadm5_ret_t
196 1.1.1.2.10.1 bouyer get_header(krb5_storage *sp, int peek, uint32_t *verp, uint32_t *tstampp,
197 1.1.1.2.10.1 bouyer enum kadm_ops *opp, uint32_t *lenp)
198 1.1.1.2.10.1 bouyer {
199 1.1.1.2.10.1 bouyer krb5_error_code ret;
200 1.1.1.2.10.1 bouyer uint32_t tstamp, op, len;
201 1.1.1.2.10.1 bouyer off_t off, new_off;
202 1.1.1.2.10.1 bouyer
203 1.1.1.2.10.1 bouyer if (tstampp == NULL)
204 1.1.1.2.10.1 bouyer tstampp = &tstamp;
205 1.1.1.2.10.1 bouyer if (lenp == NULL)
206 1.1.1.2.10.1 bouyer lenp = &len;
207 1.1.1.2.10.1 bouyer
208 1.1.1.2.10.1 bouyer *verp = 0;
209 1.1.1.2.10.1 bouyer *tstampp = 0;
210 1.1.1.2.10.1 bouyer if (opp != NULL)
211 1.1.1.2.10.1 bouyer *opp = kadm_nop;
212 1.1.1.2.10.1 bouyer *lenp = 0;
213 1.1.1.2.10.1 bouyer
214 1.1.1.2.10.1 bouyer off = krb5_storage_seek(sp, 0, SEEK_CUR);
215 1.1.1.2.10.1 bouyer if (off < 0)
216 1.1.1.2.10.1 bouyer return errno;
217 1.1.1.2.10.1 bouyer ret = krb5_ret_uint32(sp, verp);
218 1.1.1.2.10.1 bouyer if (ret == HEIM_ERR_EOF) {
219 1.1.1.2.10.1 bouyer (void) krb5_storage_seek(sp, off, SEEK_SET);
220 1.1.1.2.10.1 bouyer return HEIM_ERR_EOF;
221 1.1.1.2.10.1 bouyer }
222 1.1.1.2.10.1 bouyer if (ret)
223 1.1.1.2.10.1 bouyer goto log_corrupt;
224 1.1.1.2.10.1 bouyer ret = krb5_ret_uint32(sp, tstampp);
225 1.1.1.2.10.1 bouyer if (ret)
226 1.1.1.2.10.1 bouyer goto log_corrupt;
227 1.1.1.2.10.1 bouyer
228 1.1.1.2.10.1 bouyer /* Note: sizeof(*opp) might not == sizeof(op) */
229 1.1.1.2.10.1 bouyer ret = krb5_ret_uint32(sp, &op);
230 1.1.1.2.10.1 bouyer if (ret)
231 1.1.1.2.10.1 bouyer goto log_corrupt;
232 1.1.1.2.10.1 bouyer if (opp != NULL)
233 1.1.1.2.10.1 bouyer *opp = op;
234 1.1.1.2.10.1 bouyer
235 1.1.1.2.10.1 bouyer ret = krb5_ret_uint32(sp, lenp);
236 1.1.1.2.10.1 bouyer if (ret)
237 1.1.1.2.10.1 bouyer goto log_corrupt;
238 1.1.1.2.10.1 bouyer
239 1.1.1.2.10.1 bouyer /* Restore offset if requested */
240 1.1.1.2.10.1 bouyer if (peek == LOG_DOPEEK) {
241 1.1.1.2.10.1 bouyer new_off = krb5_storage_seek(sp, off, SEEK_SET);
242 1.1.1.2.10.1 bouyer if (new_off == -1)
243 1.1.1.2.10.1 bouyer return errno;
244 1.1.1.2.10.1 bouyer if (new_off != off)
245 1.1.1.2.10.1 bouyer return EIO;
246 1.1.1.2.10.1 bouyer }
247 1.1.1.2.10.1 bouyer
248 1.1.1.2.10.1 bouyer return 0;
249 1.1.1.2.10.1 bouyer
250 1.1.1.2.10.1 bouyer log_corrupt:
251 1.1.1.2.10.1 bouyer (void) krb5_storage_seek(sp, off, SEEK_SET);
252 1.1.1.2.10.1 bouyer return KADM5_LOG_CORRUPT;
253 1.1.1.2.10.1 bouyer }
254 1.1.1.2.10.1 bouyer
255 1.1.1.2.10.1 bouyer /*
256 1.1.1.2.10.1 bouyer * Seek to the start of the preceding record's header and returns its
257 1.1.1.2.10.1 bouyer * offset. If sp is at offset zero this sets *verp = 0 and returns 0.
258 1.1.1.2.10.1 bouyer *
259 1.1.1.2.10.1 bouyer * Does not verify the header of the previous entry.
260 1.1.1.2.10.1 bouyer *
261 1.1.1.2.10.1 bouyer * On error returns -1, setting errno (possibly to a kadm5_ret_t or
262 1.1.1.2.10.1 bouyer * krb5_error_code value) and preserves sp's offset where possible.
263 1.1.1.2.10.1 bouyer */
264 1.1.1.2.10.1 bouyer static off_t
265 1.1.1.2.10.1 bouyer seek_prev(krb5_storage *sp, uint32_t *verp, uint32_t *lenp)
266 1.1.1.2.10.1 bouyer {
267 1.1.1.2.10.1 bouyer krb5_error_code ret;
268 1.1.1.2.10.1 bouyer uint32_t len, ver;
269 1.1.1.2.10.1 bouyer off_t off_len;
270 1.1.1.2.10.1 bouyer off_t off, new_off;
271 1.1.1.2.10.1 bouyer
272 1.1.1.2.10.1 bouyer if (lenp == NULL)
273 1.1.1.2.10.1 bouyer lenp = &len;
274 1.1.1.2.10.1 bouyer if (verp == NULL)
275 1.1.1.2.10.1 bouyer verp = &ver;
276 1.1.1.2.10.1 bouyer
277 1.1.1.2.10.1 bouyer *verp = 0;
278 1.1.1.2.10.1 bouyer *lenp = 0;
279 1.1.1.2.10.1 bouyer
280 1.1.1.2.10.1 bouyer off = krb5_storage_seek(sp, 0, SEEK_CUR);
281 1.1.1.2.10.1 bouyer if (off < 0)
282 1.1.1.2.10.1 bouyer return off;
283 1.1.1.2.10.1 bouyer if (off == 0)
284 1.1.1.2.10.1 bouyer return 0;
285 1.1.1.2.10.1 bouyer
286 1.1.1.2.10.1 bouyer /* Check that `off' allows for the record's header and trailer */
287 1.1.1.2.10.1 bouyer if (off < LOG_WRAPPER_SZ)
288 1.1.1.2.10.1 bouyer goto log_corrupt;
289 1.1.1.2.10.1 bouyer
290 1.1.1.2.10.1 bouyer /* Get the previous entry's length and version from its trailer */
291 1.1.1.2.10.1 bouyer new_off = krb5_storage_seek(sp, -8, SEEK_CUR);
292 1.1.1.2.10.1 bouyer if (new_off == -1)
293 1.1.1.2.10.1 bouyer return -1;
294 1.1.1.2.10.1 bouyer if (new_off != off - 8) {
295 1.1.1.2.10.1 bouyer errno = EIO;
296 1.1.1.2.10.1 bouyer return -1;
297 1.1.1.2.10.1 bouyer }
298 1.1.1.2.10.1 bouyer ret = krb5_ret_uint32(sp, lenp);
299 1.1.1.2.10.1 bouyer if (ret)
300 1.1.1.2.10.1 bouyer goto log_corrupt;
301 1.1.1.2.10.1 bouyer
302 1.1.1.2.10.1 bouyer /* Check for overflow/sign extension */
303 1.1.1.2.10.1 bouyer off_len = (off_t)*lenp;
304 1.1.1.2.10.1 bouyer if (off_len < 0 || *lenp != (uint32_t)off_len)
305 1.1.1.2.10.1 bouyer goto log_corrupt;
306 1.1.1.2.10.1 bouyer
307 1.1.1.2.10.1 bouyer ret = krb5_ret_uint32(sp, verp);
308 1.1.1.2.10.1 bouyer if (ret)
309 1.1.1.2.10.1 bouyer goto log_corrupt;
310 1.1.1.2.10.1 bouyer
311 1.1.1.2.10.1 bouyer /* Check that `off' allows for the record */
312 1.1.1.2.10.1 bouyer if (off < LOG_WRAPPER_SZ + off_len)
313 1.1.1.2.10.1 bouyer goto log_corrupt;
314 1.1.1.2.10.1 bouyer
315 1.1.1.2.10.1 bouyer /* Seek backwards to the entry's start */
316 1.1.1.2.10.1 bouyer new_off = krb5_storage_seek(sp, -(LOG_WRAPPER_SZ + off_len), SEEK_CUR);
317 1.1.1.2.10.1 bouyer if (new_off == -1)
318 1.1.1.2.10.1 bouyer return -1;
319 1.1.1.2.10.1 bouyer if (new_off != off - (LOG_WRAPPER_SZ + off_len)) {
320 1.1.1.2.10.1 bouyer errno = EIO;
321 1.1.1.2.10.1 bouyer return -1;
322 1.1.1.2.10.1 bouyer }
323 1.1.1.2.10.1 bouyer return new_off;
324 1.1.1.2.10.1 bouyer
325 1.1.1.2.10.1 bouyer log_corrupt:
326 1.1.1.2.10.1 bouyer (void) krb5_storage_seek(sp, off, SEEK_SET);
327 1.1.1.2.10.1 bouyer errno = KADM5_LOG_CORRUPT;
328 1.1.1.2.10.1 bouyer return -1;
329 1.1.1.2.10.1 bouyer }
330 1.1.1.2.10.1 bouyer
331 1.1.1.2.10.1 bouyer /*
332 1.1.1.2.10.1 bouyer * Seek to the start of the next entry's header.
333 1.1.1.2.10.1 bouyer *
334 1.1.1.2.10.1 bouyer * On error returns -1 and preserves sp's offset.
335 1.1.1.2.10.1 bouyer */
336 1.1.1.2.10.1 bouyer static off_t
337 1.1.1.2.10.1 bouyer seek_next(krb5_storage *sp)
338 1.1.1.2.10.1 bouyer {
339 1.1.1.2.10.1 bouyer krb5_error_code ret;
340 1.1.1.2.10.1 bouyer uint32_t ver, ver2, len, len2;
341 1.1.1.2.10.1 bouyer enum kadm_ops op;
342 1.1.1.2.10.1 bouyer uint32_t tstamp;
343 1.1.1.2.10.1 bouyer off_t off, off_len, new_off;
344 1.1.1.2.10.1 bouyer
345 1.1.1.2.10.1 bouyer off = krb5_storage_seek(sp, 0, SEEK_CUR);
346 1.1.1.2.10.1 bouyer if (off < 0)
347 1.1.1.2.10.1 bouyer return off;
348 1.1.1.2.10.1 bouyer
349 1.1.1.2.10.1 bouyer errno = get_header(sp, LOG_NOPEEK, &ver, &tstamp, &op, &len);
350 1.1.1.2.10.1 bouyer if (errno)
351 1.1.1.2.10.1 bouyer return -1;
352 1.1.1.2.10.1 bouyer
353 1.1.1.2.10.1 bouyer /* Check for overflow */
354 1.1.1.2.10.1 bouyer off_len = len;
355 1.1.1.2.10.1 bouyer if (off_len < 0)
356 1.1.1.2.10.1 bouyer goto log_corrupt;
357 1.1.1.2.10.1 bouyer
358 1.1.1.2.10.1 bouyer new_off = krb5_storage_seek(sp, off_len, SEEK_CUR);
359 1.1.1.2.10.1 bouyer if (new_off == -1) {
360 1.1.1.2.10.1 bouyer (void) krb5_storage_seek(sp, off, SEEK_SET);
361 1.1.1.2.10.1 bouyer return -1;
362 1.1.1.2.10.1 bouyer }
363 1.1.1.2.10.1 bouyer if (new_off != off + LOG_HEADER_SZ + off_len)
364 1.1.1.2.10.1 bouyer goto log_corrupt;
365 1.1.1.2.10.1 bouyer ret = krb5_ret_uint32(sp, &len2);
366 1.1.1.2.10.1 bouyer if (ret || len2 != len)
367 1.1.1.2.10.1 bouyer goto log_corrupt;
368 1.1.1.2.10.1 bouyer ret = krb5_ret_uint32(sp, &ver2);
369 1.1.1.2.10.1 bouyer if (ret || ver2 != ver)
370 1.1.1.2.10.1 bouyer goto log_corrupt;
371 1.1.1.2.10.1 bouyer new_off = krb5_storage_seek(sp, 0, SEEK_CUR);
372 1.1.1.2.10.1 bouyer if (new_off == -1) {
373 1.1.1.2.10.1 bouyer (void) krb5_storage_seek(sp, off, SEEK_SET);
374 1.1.1.2.10.1 bouyer return -1;
375 1.1.1.2.10.1 bouyer }
376 1.1.1.2.10.1 bouyer if (new_off != off + off_len + LOG_WRAPPER_SZ)
377 1.1.1.2.10.1 bouyer goto log_corrupt;
378 1.1.1.2.10.1 bouyer
379 1.1.1.2.10.1 bouyer return off + off_len + LOG_WRAPPER_SZ;
380 1.1.1.2.10.1 bouyer
381 1.1.1.2.10.1 bouyer log_corrupt:
382 1.1.1.2.10.1 bouyer (void) krb5_storage_seek(sp, off, SEEK_SET);
383 1.1.1.2.10.1 bouyer errno = KADM5_LOG_CORRUPT;
384 1.1.1.2.10.1 bouyer return -1;
385 1.1.1.2.10.1 bouyer }
386 1.1.1.2.10.1 bouyer
387 1.1.1.2.10.1 bouyer /*
388 1.1.1.2.10.1 bouyer * Get the version of the entry ending at the current offset into sp.
389 1.1.1.2.10.1 bouyer * If it is the uber record, return its nominal version instead.
390 1.1.1.2.10.1 bouyer *
391 1.1.1.2.10.1 bouyer * Returns HEIM_ERR_EOF if sp is at offset zero.
392 1.1.1.2.10.1 bouyer *
393 1.1.1.2.10.1 bouyer * Preserves sp's offset.
394 1.1 elric */
395 1.1.1.2.10.1 bouyer static kadm5_ret_t
396 1.1.1.2.10.1 bouyer get_version_prev(krb5_storage *sp, uint32_t *verp, uint32_t *tstampp)
397 1.1.1.2.10.1 bouyer {
398 1.1.1.2.10.1 bouyer krb5_error_code ret;
399 1.1.1.2.10.1 bouyer uint32_t ver, ver2, len, len2;
400 1.1.1.2.10.1 bouyer off_t off, prev_off, new_off;
401 1.1.1.2.10.1 bouyer
402 1.1.1.2.10.1 bouyer *verp = 0;
403 1.1.1.2.10.1 bouyer if (tstampp != NULL)
404 1.1.1.2.10.1 bouyer *tstampp = 0;
405 1.1.1.2.10.1 bouyer
406 1.1.1.2.10.1 bouyer off = krb5_storage_seek(sp, 0, SEEK_CUR);
407 1.1.1.2.10.1 bouyer if (off < 0)
408 1.1.1.2.10.1 bouyer return errno;
409 1.1.1.2.10.1 bouyer if (off == 0)
410 1.1.1.2.10.1 bouyer return HEIM_ERR_EOF;
411 1.1.1.2.10.1 bouyer
412 1.1.1.2.10.1 bouyer /* Read the trailer and seek back */
413 1.1.1.2.10.1 bouyer prev_off = seek_prev(sp, &ver, &len);
414 1.1.1.2.10.1 bouyer if (prev_off == -1)
415 1.1.1.2.10.1 bouyer return errno;
416 1.1.1.2.10.1 bouyer
417 1.1.1.2.10.1 bouyer /* Uber record? Return nominal version. */
418 1.1.1.2.10.1 bouyer if (prev_off == 0 && len == LOG_UBER_LEN && ver == 0) {
419 1.1.1.2.10.1 bouyer /* Skip 8 byte offset and 4 byte time */
420 1.1.1.2.10.1 bouyer if (krb5_storage_seek(sp, LOG_HEADER_SZ + 12, SEEK_SET)
421 1.1.1.2.10.1 bouyer != LOG_HEADER_SZ + 12)
422 1.1.1.2.10.1 bouyer return errno;
423 1.1.1.2.10.1 bouyer ret = krb5_ret_uint32(sp, verp);
424 1.1.1.2.10.1 bouyer if (krb5_storage_seek(sp, 0, SEEK_SET) != 0)
425 1.1.1.2.10.1 bouyer return errno;
426 1.1.1.2.10.1 bouyer if (ret != 0)
427 1.1.1.2.10.1 bouyer return ret;
428 1.1.1.2.10.1 bouyer } else {
429 1.1.1.2.10.1 bouyer *verp = ver;
430 1.1.1.2.10.1 bouyer }
431 1.1.1.2.10.1 bouyer
432 1.1.1.2.10.1 bouyer /* Verify that the trailer matches header */
433 1.1.1.2.10.1 bouyer ret = get_header(sp, LOG_NOPEEK, &ver2, tstampp, NULL, &len2);
434 1.1.1.2.10.1 bouyer if (ret || ver != ver2 || len != len2)
435 1.1.1.2.10.1 bouyer goto log_corrupt;
436 1.1.1.2.10.1 bouyer
437 1.1.1.2.10.1 bouyer /* Preserve offset */
438 1.1.1.2.10.1 bouyer new_off = krb5_storage_seek(sp, off, SEEK_SET);
439 1.1.1.2.10.1 bouyer if (new_off == -1)
440 1.1.1.2.10.1 bouyer return errno;
441 1.1.1.2.10.1 bouyer if (new_off != off) {
442 1.1.1.2.10.1 bouyer errno = EIO;
443 1.1.1.2.10.1 bouyer return errno;
444 1.1.1.2.10.1 bouyer }
445 1.1.1.2.10.1 bouyer return 0;
446 1.1.1.2.10.1 bouyer
447 1.1.1.2.10.1 bouyer log_corrupt:
448 1.1.1.2.10.1 bouyer (void) krb5_storage_seek(sp, off, SEEK_SET);
449 1.1.1.2.10.1 bouyer return KADM5_LOG_CORRUPT;
450 1.1.1.2.10.1 bouyer }
451 1.1.1.2.10.1 bouyer
452 1.1.1.2.10.1 bouyer static size_t
453 1.1.1.2.10.1 bouyer get_max_log_size(krb5_context context)
454 1.1.1.2.10.1 bouyer {
455 1.1.1.2.10.1 bouyer off_t n;
456 1.1.1.2.10.1 bouyer
457 1.1.1.2.10.1 bouyer /* Use database-label-specific lookup? No, ETOOHARD. */
458 1.1.1.2.10.1 bouyer /* Default to 50MB max log size */
459 1.1.1.2.10.1 bouyer n = krb5_config_get_int_default(context, NULL, 52428800,
460 1.1.1.2.10.1 bouyer "kdc",
461 1.1.1.2.10.1 bouyer "log-max-size",
462 1.1.1.2.10.1 bouyer NULL);
463 1.1.1.2.10.1 bouyer if (n >= 4 * (LOG_UBER_LEN + LOG_WRAPPER_SZ) && n == (size_t)n)
464 1.1.1.2.10.1 bouyer return (size_t)n;
465 1.1.1.2.10.1 bouyer return 0;
466 1.1.1.2.10.1 bouyer }
467 1.1.1.2.10.1 bouyer
468 1.1.1.2.10.1 bouyer static kadm5_ret_t truncate_if_needed(kadm5_server_context *);
469 1.1.1.2.10.1 bouyer static krb5_storage *log_goto_first(kadm5_server_context *, int);
470 1.1 elric
471 1.1.1.2.10.1 bouyer /*
472 1.1.1.2.10.1 bouyer * Get the version and timestamp metadata of either the first, or last
473 1.1.1.2.10.1 bouyer * confirmed entry in the log.
474 1.1.1.2.10.1 bouyer *
475 1.1.1.2.10.1 bouyer * If `which' is LOG_VERSION_UBER, then this gets the version number of the uber
476 1.1.1.2.10.1 bouyer * uber record which must be 0, or else we need to upgrade the log.
477 1.1.1.2.10.1 bouyer *
478 1.1.1.2.10.1 bouyer * If `which' is LOG_VERSION_FIRST, then this gets the metadata for the
479 1.1.1.2.10.1 bouyer * logically first entry past the uberblock, or returns HEIM_EOF if
480 1.1.1.2.10.1 bouyer * only the uber record is present.
481 1.1.1.2.10.1 bouyer *
482 1.1.1.2.10.1 bouyer * If `which' is LOG_VERSION_LAST, then this gets metadata for the last
483 1.1.1.2.10.1 bouyer * confirmed entry's version and timestamp. If only the uber record is present,
484 1.1.1.2.10.1 bouyer * then the version will be its "nominal" version, which may differ from its
485 1.1.1.2.10.1 bouyer * actual version (0).
486 1.1.1.2.10.1 bouyer *
487 1.1.1.2.10.1 bouyer * The `fd''s offset will be set to the start of the header of the entry
488 1.1.1.2.10.1 bouyer * identified by `which'.
489 1.1.1.2.10.1 bouyer */
490 1.1 elric kadm5_ret_t
491 1.1.1.2.10.1 bouyer kadm5_log_get_version_fd(kadm5_server_context *server_context, int fd,
492 1.1.1.2.10.1 bouyer int which, uint32_t *ver, uint32_t *tstamp)
493 1.1 elric {
494 1.1.1.2.10.1 bouyer kadm5_ret_t ret = 0;
495 1.1 elric krb5_storage *sp;
496 1.1.1.2.10.1 bouyer enum kadm_ops op = kadm_get;
497 1.1.1.2.10.1 bouyer uint32_t len = 0;
498 1.1.1.2.10.1 bouyer uint32_t tmp;
499 1.1.1.2.10.1 bouyer
500 1.1.1.2.10.1 bouyer if (fd == -1)
501 1.1.1.2.10.1 bouyer return 0; /* /dev/null */
502 1.1.1.2.10.1 bouyer
503 1.1.1.2.10.1 bouyer if (tstamp == NULL)
504 1.1.1.2.10.1 bouyer tstamp = &tmp;
505 1.1.1.2.10.1 bouyer
506 1.1.1.2.10.1 bouyer *ver = 0;
507 1.1.1.2.10.1 bouyer *tstamp = 0;
508 1.1.1.2.10.1 bouyer
509 1.1.1.2.10.1 bouyer switch (which) {
510 1.1.1.2.10.1 bouyer case LOG_VERSION_LAST:
511 1.1.1.2.10.1 bouyer sp = kadm5_log_goto_end(server_context, fd);
512 1.1.1.2.10.1 bouyer if (sp == NULL)
513 1.1.1.2.10.1 bouyer return errno;
514 1.1.1.2.10.1 bouyer ret = get_version_prev(sp, ver, tstamp);
515 1.1.1.2.10.1 bouyer krb5_storage_free(sp);
516 1.1.1.2.10.1 bouyer break;
517 1.1.1.2.10.1 bouyer case LOG_VERSION_FIRST:
518 1.1.1.2.10.1 bouyer sp = log_goto_first(server_context, fd);
519 1.1.1.2.10.1 bouyer if (sp == NULL)
520 1.1.1.2.10.1 bouyer return errno;
521 1.1.1.2.10.1 bouyer ret = get_header(sp, LOG_DOPEEK, ver, tstamp, NULL, NULL);
522 1.1.1.2.10.1 bouyer krb5_storage_free(sp);
523 1.1.1.2.10.1 bouyer break;
524 1.1.1.2.10.1 bouyer case LOG_VERSION_UBER:
525 1.1.1.2.10.1 bouyer sp = krb5_storage_from_fd(server_context->log_context.log_fd);
526 1.1.1.2.10.1 bouyer if (sp == NULL)
527 1.1.1.2.10.1 bouyer return errno;
528 1.1.1.2.10.1 bouyer if (krb5_storage_seek(sp, 0, SEEK_SET) == 0)
529 1.1.1.2.10.1 bouyer ret = get_header(sp, LOG_DOPEEK, ver, tstamp, &op, &len);
530 1.1.1.2.10.1 bouyer else
531 1.1.1.2.10.1 bouyer ret = errno;
532 1.1.1.2.10.1 bouyer if (ret == 0 && (op != kadm_nop || len != LOG_UBER_LEN || *ver != 0))
533 1.1.1.2.10.1 bouyer ret = KADM5_LOG_NEEDS_UPGRADE;
534 1.1.1.2.10.1 bouyer krb5_storage_free(sp);
535 1.1.1.2.10.1 bouyer break;
536 1.1.1.2.10.1 bouyer default:
537 1.1.1.2.10.1 bouyer return ENOTSUP;
538 1.1.1.2.10.1 bouyer }
539 1.1 elric
540 1.1.1.2.10.1 bouyer return ret;
541 1.1 elric }
542 1.1 elric
543 1.1.1.2.10.1 bouyer /* Get the version of the last confirmed entry in the log */
544 1.1 elric kadm5_ret_t
545 1.1.1.2.10.1 bouyer kadm5_log_get_version(kadm5_server_context *server_context, uint32_t *ver)
546 1.1 elric {
547 1.1.1.2.10.1 bouyer return kadm5_log_get_version_fd(server_context,
548 1.1.1.2.10.1 bouyer server_context->log_context.log_fd,
549 1.1.1.2.10.1 bouyer LOG_VERSION_LAST, ver, NULL);
550 1.1 elric }
551 1.1 elric
552 1.1.1.2.10.1 bouyer /* Sets the version in the context, but NOT in the log */
553 1.1 elric kadm5_ret_t
554 1.1.1.2.10.1 bouyer kadm5_log_set_version(kadm5_server_context *context, uint32_t vno)
555 1.1 elric {
556 1.1 elric kadm5_log_context *log_context = &context->log_context;
557 1.1 elric
558 1.1 elric log_context->version = vno;
559 1.1 elric return 0;
560 1.1 elric }
561 1.1 elric
562 1.1.1.2.10.1 bouyer /*
563 1.1.1.2.10.1 bouyer * Open the log and setup server_context->log_context
564 1.1.1.2.10.1 bouyer */
565 1.1.1.2.10.1 bouyer static kadm5_ret_t
566 1.1.1.2.10.1 bouyer log_open(kadm5_server_context *server_context, int lock_mode)
567 1.1 elric {
568 1.1.1.2.10.1 bouyer int fd = -1;
569 1.1.1.2.10.1 bouyer int lock_it = 0;
570 1.1.1.2.10.1 bouyer int lock_nb = 0;
571 1.1.1.2.10.1 bouyer int oflags = O_RDWR;
572 1.1 elric kadm5_ret_t ret;
573 1.1.1.2.10.1 bouyer kadm5_log_context *log_context = &server_context->log_context;
574 1.1 elric
575 1.1.1.2.10.1 bouyer if (lock_mode & LOCK_NB) {
576 1.1.1.2.10.1 bouyer lock_mode &= ~LOCK_NB;
577 1.1.1.2.10.1 bouyer lock_nb = LOCK_NB;
578 1.1 elric }
579 1.1.1.2.10.1 bouyer
580 1.1.1.2.10.1 bouyer if (lock_mode == log_context->lock_mode && log_context->log_fd != -1)
581 1.1.1.2.10.1 bouyer return 0;
582 1.1.1.2.10.1 bouyer
583 1.1.1.2.10.1 bouyer if (strcmp(log_context->log_file, "/dev/null") == 0) {
584 1.1.1.2.10.1 bouyer /* log_context->log_fd should be -1 here */
585 1.1.1.2.10.1 bouyer return 0;
586 1.1 elric }
587 1.1 elric
588 1.1.1.2.10.1 bouyer if (log_context->log_fd != -1) {
589 1.1.1.2.10.1 bouyer /* Lock or change lock */
590 1.1.1.2.10.1 bouyer fd = log_context->log_fd;
591 1.1.1.2.10.1 bouyer if (lseek(fd, 0, SEEK_SET) == -1)
592 1.1.1.2.10.1 bouyer return errno;
593 1.1.1.2.10.1 bouyer lock_it = (lock_mode != log_context->lock_mode);
594 1.1.1.2.10.1 bouyer } else {
595 1.1.1.2.10.1 bouyer /* Open and lock */
596 1.1.1.2.10.1 bouyer if (lock_mode != LOCK_UN)
597 1.1.1.2.10.1 bouyer oflags |= O_CREAT;
598 1.1.1.2.10.1 bouyer fd = open(log_context->log_file, oflags, 0600);
599 1.1.1.2.10.1 bouyer if (fd < 0) {
600 1.1.1.2.10.1 bouyer ret = errno;
601 1.1.1.2.10.1 bouyer krb5_set_error_message(server_context->context, ret,
602 1.1.1.2.10.1 bouyer "log_open: open %s", log_context->log_file);
603 1.1.1.2.10.1 bouyer return ret;
604 1.1.1.2.10.1 bouyer }
605 1.1.1.2.10.1 bouyer lock_it = (lock_mode != LOCK_UN);
606 1.1.1.2.10.1 bouyer }
607 1.1.1.2.10.1 bouyer if (lock_it && flock(fd, lock_mode | lock_nb) < 0) {
608 1.1.1.2.10.1 bouyer ret = errno;
609 1.1.1.2.10.1 bouyer krb5_set_error_message(server_context->context, ret,
610 1.1.1.2.10.1 bouyer "log_open: flock %s", log_context->log_file);
611 1.1.1.2.10.1 bouyer if (fd != log_context->log_fd)
612 1.1.1.2.10.1 bouyer (void) close(fd);
613 1.1 elric return ret;
614 1.1.1.2.10.1 bouyer }
615 1.1.1.2.10.1 bouyer
616 1.1.1.2.10.1 bouyer log_context->log_fd = fd;
617 1.1.1.2.10.1 bouyer log_context->lock_mode = lock_mode;
618 1.1.1.2.10.1 bouyer log_context->read_only = (lock_mode != LOCK_EX);
619 1.1 elric
620 1.1 elric return 0;
621 1.1 elric }
622 1.1 elric
623 1.1.1.2.10.1 bouyer /*
624 1.1.1.2.10.1 bouyer * Open the log and setup server_context->log_context
625 1.1.1.2.10.1 bouyer */
626 1.1.1.2.10.1 bouyer static kadm5_ret_t
627 1.1.1.2.10.1 bouyer log_init(kadm5_server_context *server_context, int lock_mode)
628 1.1 elric {
629 1.1 elric int fd;
630 1.1.1.2.10.1 bouyer struct stat st;
631 1.1.1.2.10.1 bouyer uint32_t vno;
632 1.1.1.2.10.1 bouyer size_t maxbytes = get_max_log_size(server_context->context);
633 1.1.1.2.10.1 bouyer kadm5_ret_t ret;
634 1.1.1.2.10.1 bouyer kadm5_log_context *log_context = &server_context->log_context;
635 1.1 elric
636 1.1.1.2.10.1 bouyer if (strcmp(log_context->log_file, "/dev/null") == 0) {
637 1.1.1.2.10.1 bouyer /* log_context->log_fd should be -1 here */
638 1.1.1.2.10.1 bouyer return 0;
639 1.1.1.2.10.1 bouyer }
640 1.1.1.2.10.1 bouyer
641 1.1.1.2.10.1 bouyer ret = log_open(server_context, lock_mode);
642 1.1.1.2.10.1 bouyer if (ret)
643 1.1.1.2.10.1 bouyer return ret;
644 1.1.1.2.10.1 bouyer
645 1.1.1.2.10.1 bouyer fd = log_context->log_fd;
646 1.1.1.2.10.1 bouyer if (!log_context->read_only) {
647 1.1.1.2.10.1 bouyer if (fstat(fd, &st) == -1)
648 1.1.1.2.10.1 bouyer ret = errno;
649 1.1.1.2.10.1 bouyer if (ret == 0 && st.st_size == 0) {
650 1.1.1.2.10.1 bouyer /* Write first entry */
651 1.1.1.2.10.1 bouyer log_context->version = 0;
652 1.1.1.2.10.1 bouyer ret = kadm5_log_nop(server_context, kadm_nop_plain);
653 1.1.1.2.10.1 bouyer if (ret == 0)
654 1.1.1.2.10.1 bouyer return 0; /* no need to truncate_if_needed(): it's not */
655 1.1.1.2.10.1 bouyer }
656 1.1.1.2.10.1 bouyer if (ret == 0) {
657 1.1.1.2.10.1 bouyer ret = kadm5_log_get_version_fd(server_context, fd,
658 1.1.1.2.10.1 bouyer LOG_VERSION_UBER, &vno, NULL);
659 1.1.1.2.10.1 bouyer
660 1.1.1.2.10.1 bouyer /* Upgrade the log if it was an old-style log */
661 1.1.1.2.10.1 bouyer if (ret == KADM5_LOG_NEEDS_UPGRADE)
662 1.1.1.2.10.1 bouyer ret = kadm5_log_truncate(server_context, 0, maxbytes / 4);
663 1.1.1.2.10.1 bouyer }
664 1.1.1.2.10.1 bouyer if (ret == 0)
665 1.1.1.2.10.1 bouyer ret = kadm5_log_recover(server_context, kadm_recover_replay);
666 1.1.1.2.10.1 bouyer }
667 1.1.1.2.10.1 bouyer
668 1.1.1.2.10.1 bouyer if (ret == 0) {
669 1.1.1.2.10.1 bouyer ret = kadm5_log_get_version_fd(server_context, fd, LOG_VERSION_LAST,
670 1.1.1.2.10.1 bouyer &log_context->version, NULL);
671 1.1.1.2.10.1 bouyer if (ret == HEIM_ERR_EOF)
672 1.1.1.2.10.1 bouyer ret = 0;
673 1.1 elric }
674 1.1 elric
675 1.1.1.2.10.1 bouyer if (ret == 0)
676 1.1.1.2.10.1 bouyer ret = truncate_if_needed(server_context);
677 1.1.1.2.10.1 bouyer
678 1.1.1.2.10.1 bouyer if (ret != 0)
679 1.1.1.2.10.1 bouyer (void) kadm5_log_end(server_context);
680 1.1.1.2.10.1 bouyer return ret;
681 1.1 elric }
682 1.1 elric
683 1.1.1.2.10.1 bouyer /* Open the log with an exclusive lock */
684 1.1.1.2.10.1 bouyer kadm5_ret_t
685 1.1.1.2.10.1 bouyer kadm5_log_init(kadm5_server_context *server_context)
686 1.1.1.2.10.1 bouyer {
687 1.1.1.2.10.1 bouyer return log_init(server_context, LOCK_EX);
688 1.1.1.2.10.1 bouyer }
689 1.1 elric
690 1.1.1.2.10.1 bouyer /* Open the log with an exclusive non-blocking lock */
691 1.1 elric kadm5_ret_t
692 1.1.1.2.10.1 bouyer kadm5_log_init_nb(kadm5_server_context *server_context)
693 1.1 elric {
694 1.1.1.2.10.1 bouyer return log_init(server_context, LOCK_EX | LOCK_NB);
695 1.1.1.2.10.1 bouyer }
696 1.1.1.2.10.1 bouyer
697 1.1.1.2.10.1 bouyer /* Open the log with no locks */
698 1.1.1.2.10.1 bouyer kadm5_ret_t
699 1.1.1.2.10.1 bouyer kadm5_log_init_nolock(kadm5_server_context *server_context)
700 1.1.1.2.10.1 bouyer {
701 1.1.1.2.10.1 bouyer return log_init(server_context, LOCK_UN);
702 1.1.1.2.10.1 bouyer }
703 1.1.1.2.10.1 bouyer
704 1.1.1.2.10.1 bouyer /* Open the log with a shared lock */
705 1.1.1.2.10.1 bouyer kadm5_ret_t
706 1.1.1.2.10.1 bouyer kadm5_log_init_sharedlock(kadm5_server_context *server_context, int lock_flags)
707 1.1.1.2.10.1 bouyer {
708 1.1.1.2.10.1 bouyer return log_init(server_context, LOCK_SH | lock_flags);
709 1.1.1.2.10.1 bouyer }
710 1.1.1.2.10.1 bouyer
711 1.1.1.2.10.1 bouyer /*
712 1.1.1.2.10.1 bouyer * Reinitialize the log and open it
713 1.1.1.2.10.1 bouyer */
714 1.1.1.2.10.1 bouyer kadm5_ret_t
715 1.1.1.2.10.1 bouyer kadm5_log_reinit(kadm5_server_context *server_context, uint32_t vno)
716 1.1.1.2.10.1 bouyer {
717 1.1.1.2.10.1 bouyer int ret;
718 1.1.1.2.10.1 bouyer kadm5_log_context *log_context = &server_context->log_context;
719 1.1.1.2.10.1 bouyer
720 1.1.1.2.10.1 bouyer ret = log_open(server_context, LOCK_EX);
721 1.1.1.2.10.1 bouyer if (ret)
722 1.1.1.2.10.1 bouyer return ret;
723 1.1.1.2.10.1 bouyer if (log_context->log_fd != -1) {
724 1.1.1.2.10.1 bouyer if (ftruncate(log_context->log_fd, 0) < 0) {
725 1.1.1.2.10.1 bouyer ret = errno;
726 1.1.1.2.10.1 bouyer return ret;
727 1.1.1.2.10.1 bouyer }
728 1.1.1.2.10.1 bouyer if (lseek(log_context->log_fd, 0, SEEK_SET) < 0) {
729 1.1.1.2.10.1 bouyer ret = errno;
730 1.1.1.2.10.1 bouyer return ret;
731 1.1.1.2.10.1 bouyer }
732 1.1.1.2.10.1 bouyer }
733 1.1.1.2.10.1 bouyer
734 1.1.1.2.10.1 bouyer /* Write uber entry and truncation nop with version `vno` */
735 1.1.1.2.10.1 bouyer log_context->version = vno;
736 1.1.1.2.10.1 bouyer return kadm5_log_nop(server_context, kadm_nop_plain);
737 1.1.1.2.10.1 bouyer }
738 1.1.1.2.10.1 bouyer
739 1.1.1.2.10.1 bouyer /* Close the server_context->log_context. */
740 1.1.1.2.10.1 bouyer kadm5_ret_t
741 1.1.1.2.10.1 bouyer kadm5_log_end(kadm5_server_context *server_context)
742 1.1.1.2.10.1 bouyer {
743 1.1.1.2.10.1 bouyer kadm5_log_context *log_context = &server_context->log_context;
744 1.1.1.2.10.1 bouyer kadm5_ret_t ret = 0;
745 1.1 elric int fd = log_context->log_fd;
746 1.1 elric
747 1.1.1.2.10.1 bouyer if (fd != -1) {
748 1.1.1.2.10.1 bouyer if (log_context->lock_mode != LOCK_UN) {
749 1.1.1.2.10.1 bouyer if (flock(fd, LOCK_UN) == -1 && errno == EBADF)
750 1.1.1.2.10.1 bouyer ret = errno;
751 1.1.1.2.10.1 bouyer }
752 1.1.1.2.10.1 bouyer if (ret != EBADF && close(fd) == -1)
753 1.1.1.2.10.1 bouyer ret = errno;
754 1.1.1.2.10.1 bouyer }
755 1.1 elric log_context->log_fd = -1;
756 1.1.1.2.10.1 bouyer log_context->lock_mode = LOCK_UN;
757 1.1.1.2.10.1 bouyer return ret;
758 1.1 elric }
759 1.1 elric
760 1.1.1.2.10.1 bouyer /*
761 1.1.1.2.10.1 bouyer * Write the version, timestamp, and op for a new entry.
762 1.1.1.2.10.1 bouyer *
763 1.1.1.2.10.1 bouyer * Note that the sp should be a krb5_storage_emem(), not a file.
764 1.1.1.2.10.1 bouyer *
765 1.1.1.2.10.1 bouyer * On success the sp's offset will be where the length of the payload
766 1.1.1.2.10.1 bouyer * should be written.
767 1.1.1.2.10.1 bouyer */
768 1.1 elric static kadm5_ret_t
769 1.1.1.2.10.1 bouyer kadm5_log_preamble(kadm5_server_context *context,
770 1.1.1.2.10.1 bouyer krb5_storage *sp,
771 1.1.1.2.10.1 bouyer enum kadm_ops op,
772 1.1.1.2.10.1 bouyer uint32_t vno)
773 1.1 elric {
774 1.1 elric kadm5_log_context *log_context = &context->log_context;
775 1.1.1.2.10.1 bouyer time_t now = time(NULL);
776 1.1.1.2.10.1 bouyer kadm5_ret_t ret;
777 1.1 elric
778 1.1.1.2.10.1 bouyer ret = krb5_store_uint32(sp, vno);
779 1.1.1.2.10.1 bouyer if (ret)
780 1.1.1.2.10.1 bouyer return ret;
781 1.1.1.2.10.1 bouyer ret = krb5_store_uint32(sp, now);
782 1.1.1.2.10.1 bouyer if (ret)
783 1.1.1.2.10.1 bouyer return ret;
784 1.1.1.2.10.1 bouyer log_context->last_time = now;
785 1.1.1.2.10.1 bouyer
786 1.1.1.2.10.1 bouyer if (op < kadm_first || op > kadm_last)
787 1.1.1.2.10.1 bouyer return ERANGE;
788 1.1.1.2.10.1 bouyer return krb5_store_uint32(sp, op);
789 1.1 elric }
790 1.1 elric
791 1.1.1.2.10.1 bouyer /* Writes the version part of the trailer */
792 1.1 elric static kadm5_ret_t
793 1.1.1.2.10.1 bouyer kadm5_log_postamble(kadm5_log_context *context,
794 1.1.1.2.10.1 bouyer krb5_storage *sp,
795 1.1.1.2.10.1 bouyer uint32_t vno)
796 1.1 elric {
797 1.1.1.2.10.1 bouyer return krb5_store_uint32(sp, vno);
798 1.1 elric }
799 1.1 elric
800 1.1 elric /*
801 1.1.1.2.10.1 bouyer * Signal the ipropd-master about changes to the log.
802 1.1.1.2.10.1 bouyer */
803 1.1.1.2.10.1 bouyer /*
804 1.1.1.2.10.1 bouyer * XXX Get rid of the ifdef by having a sockaddr in log_context in both
805 1.1.1.2.10.1 bouyer * cases.
806 1.1.1.2.10.1 bouyer *
807 1.1.1.2.10.1 bouyer * XXX Better yet, just connect to the master's socket that slaves
808 1.1.1.2.10.1 bouyer * connect to, and then disconnect. The master should then check the
809 1.1.1.2.10.1 bouyer * log on every connection accepted. Then we wouldn't need IPC to
810 1.1.1.2.10.1 bouyer * signal the master.
811 1.1.1.2.10.1 bouyer */
812 1.1.1.2.10.1 bouyer void
813 1.1.1.2.10.1 bouyer kadm5_log_signal_master(kadm5_server_context *context)
814 1.1.1.2.10.1 bouyer {
815 1.1.1.2.10.1 bouyer kadm5_log_context *log_context = &context->log_context;
816 1.1.1.2.10.1 bouyer #ifndef NO_UNIX_SOCKETS
817 1.1.1.2.10.1 bouyer sendto(log_context->socket_fd,
818 1.1.1.2.10.1 bouyer (void *)&log_context->version,
819 1.1.1.2.10.1 bouyer sizeof(log_context->version),
820 1.1.1.2.10.1 bouyer 0,
821 1.1.1.2.10.1 bouyer (struct sockaddr *)&log_context->socket_name,
822 1.1.1.2.10.1 bouyer sizeof(log_context->socket_name));
823 1.1.1.2.10.1 bouyer #else
824 1.1.1.2.10.1 bouyer sendto(log_context->socket_fd,
825 1.1.1.2.10.1 bouyer (void *)&log_context->version,
826 1.1.1.2.10.1 bouyer sizeof(log_context->version),
827 1.1.1.2.10.1 bouyer 0,
828 1.1.1.2.10.1 bouyer log_context->socket_info->ai_addr,
829 1.1.1.2.10.1 bouyer log_context->socket_info->ai_addrlen);
830 1.1.1.2.10.1 bouyer #endif
831 1.1.1.2.10.1 bouyer }
832 1.1.1.2.10.1 bouyer
833 1.1.1.2.10.1 bouyer /*
834 1.1.1.2.10.1 bouyer * Write sp's contents (which must be a fully formed record, complete
835 1.1.1.2.10.1 bouyer * with header, payload, and trailer) to the log and fsync the log.
836 1.1.1.2.10.1 bouyer *
837 1.1.1.2.10.1 bouyer * Does not free sp.
838 1.1 elric */
839 1.1 elric
840 1.1 elric static kadm5_ret_t
841 1.1.1.2.10.1 bouyer kadm5_log_flush(kadm5_server_context *context, krb5_storage *sp)
842 1.1 elric {
843 1.1.1.2.10.1 bouyer kadm5_log_context *log_context = &context->log_context;
844 1.1.1.2.10.1 bouyer kadm5_ret_t ret;
845 1.1 elric krb5_data data;
846 1.1 elric size_t len;
847 1.1.1.2.10.1 bouyer krb5_ssize_t bytes;
848 1.1.1.2.10.1 bouyer uint32_t new_ver, prev_ver;
849 1.1.1.2.10.1 bouyer off_t off, end;
850 1.1.1.2.10.1 bouyer
851 1.1.1.2.10.1 bouyer if (strcmp(log_context->log_file, "/dev/null") == 0)
852 1.1.1.2.10.1 bouyer return 0;
853 1.1.1.2.10.1 bouyer
854 1.1.1.2.10.1 bouyer if (log_context->read_only)
855 1.1.1.2.10.1 bouyer return EROFS;
856 1.1.1.2.10.1 bouyer
857 1.1.1.2.10.1 bouyer if (krb5_storage_seek(sp, 0, SEEK_SET) == -1)
858 1.1.1.2.10.1 bouyer return errno;
859 1.1.1.2.10.1 bouyer
860 1.1.1.2.10.1 bouyer ret = get_header(sp, LOG_DOPEEK, &new_ver, NULL, NULL, NULL);
861 1.1.1.2.10.1 bouyer if (ret)
862 1.1.1.2.10.1 bouyer return ret;
863 1.1.1.2.10.1 bouyer
864 1.1.1.2.10.1 bouyer ret = krb5_storage_to_data(sp, &data);
865 1.1.1.2.10.1 bouyer if (ret)
866 1.1.1.2.10.1 bouyer return ret;
867 1.1.1.2.10.1 bouyer
868 1.1.1.2.10.1 bouyer /* Abandon the emem storage reference */
869 1.1.1.2.10.1 bouyer sp = krb5_storage_from_fd(log_context->log_fd);
870 1.1.1.2.10.1 bouyer if (sp == NULL) {
871 1.1.1.2.10.1 bouyer krb5_data_free(&data);
872 1.1.1.2.10.1 bouyer return ENOMEM;
873 1.1.1.2.10.1 bouyer }
874 1.1.1.2.10.1 bouyer
875 1.1.1.2.10.1 bouyer /* Check that we are at the end of the log and fail if not */
876 1.1.1.2.10.1 bouyer off = krb5_storage_seek(sp, 0, SEEK_CUR);
877 1.1.1.2.10.1 bouyer if (off == -1) {
878 1.1.1.2.10.1 bouyer krb5_data_free(&data);
879 1.1.1.2.10.1 bouyer krb5_storage_free(sp);
880 1.1.1.2.10.1 bouyer return errno;
881 1.1.1.2.10.1 bouyer }
882 1.1.1.2.10.1 bouyer end = krb5_storage_seek(sp, 0, SEEK_END);
883 1.1.1.2.10.1 bouyer if (end == -1) {
884 1.1.1.2.10.1 bouyer krb5_data_free(&data);
885 1.1.1.2.10.1 bouyer krb5_storage_free(sp);
886 1.1.1.2.10.1 bouyer return errno;
887 1.1.1.2.10.1 bouyer }
888 1.1.1.2.10.1 bouyer if (end != off) {
889 1.1.1.2.10.1 bouyer krb5_data_free(&data);
890 1.1.1.2.10.1 bouyer krb5_storage_free(sp);
891 1.1.1.2.10.1 bouyer return KADM5_LOG_CORRUPT;
892 1.1.1.2.10.1 bouyer }
893 1.1.1.2.10.1 bouyer
894 1.1.1.2.10.1 bouyer /* Enforce monotonically incremented versioning of records */
895 1.1.1.2.10.1 bouyer if (seek_prev(sp, &prev_ver, NULL) == -1 ||
896 1.1.1.2.10.1 bouyer krb5_storage_seek(sp, end, SEEK_SET) == -1) {
897 1.1.1.2.10.1 bouyer ret = errno;
898 1.1.1.2.10.1 bouyer krb5_data_free(&data);
899 1.1.1.2.10.1 bouyer krb5_storage_free(sp);
900 1.1.1.2.10.1 bouyer return ret;
901 1.1.1.2.10.1 bouyer }
902 1.1.1.2.10.1 bouyer
903 1.1.1.2.10.1 bouyer if (prev_ver != 0 && prev_ver != log_context->version)
904 1.1.1.2.10.1 bouyer return EINVAL; /* Internal error, really; just a consistency check */
905 1.1.1.2.10.1 bouyer
906 1.1.1.2.10.1 bouyer if (prev_ver != 0 && new_ver != prev_ver + 1) {
907 1.1.1.2.10.1 bouyer krb5_warnx(context->context, "refusing to write a log record "
908 1.1.1.2.10.1 bouyer "with non-monotonic version (new: %u, old: %u)",
909 1.1.1.2.10.1 bouyer new_ver, prev_ver);
910 1.1.1.2.10.1 bouyer return KADM5_LOG_CORRUPT;
911 1.1.1.2.10.1 bouyer }
912 1.1 elric
913 1.1 elric len = data.length;
914 1.1.1.2.10.1 bouyer bytes = krb5_storage_write(sp, data.data, len);
915 1.1.1.2.10.1 bouyer krb5_data_free(&data);
916 1.1.1.2.10.1 bouyer if (bytes < 0) {
917 1.1.1.2.10.1 bouyer krb5_storage_free(sp);
918 1.1 elric return errno;
919 1.1 elric }
920 1.1.1.2.10.1 bouyer if (bytes != (krb5_ssize_t)len) {
921 1.1.1.2.10.1 bouyer krb5_storage_free(sp);
922 1.1.1.2.10.1 bouyer return EIO;
923 1.1 elric }
924 1.1 elric
925 1.1.1.2.10.1 bouyer ret = krb5_storage_fsync(sp);
926 1.1.1.2.10.1 bouyer krb5_storage_free(sp);
927 1.1.1.2.10.1 bouyer if (ret)
928 1.1.1.2.10.1 bouyer return ret;
929 1.1 elric
930 1.1.1.2.10.1 bouyer /* Retain the nominal database version when flushing the uber record */
931 1.1.1.2.10.1 bouyer if (new_ver != 0)
932 1.1.1.2.10.1 bouyer log_context->version = new_ver;
933 1.1 elric return 0;
934 1.1 elric }
935 1.1 elric
936 1.1 elric /*
937 1.1.1.2.10.1 bouyer * Add a `create' operation to the log and perform the create against the HDB.
938 1.1 elric */
939 1.1 elric kadm5_ret_t
940 1.1.1.2.10.1 bouyer kadm5_log_create(kadm5_server_context *context, hdb_entry *entry)
941 1.1 elric {
942 1.1 elric krb5_storage *sp;
943 1.1 elric kadm5_ret_t ret;
944 1.1 elric krb5_data value;
945 1.1.1.2.10.1 bouyer hdb_entry_ex ent;
946 1.1 elric kadm5_log_context *log_context = &context->log_context;
947 1.1 elric
948 1.1.1.2.10.1 bouyer memset(&ent, 0, sizeof(ent));
949 1.1.1.2.10.1 bouyer ent.ctx = 0;
950 1.1.1.2.10.1 bouyer ent.free_entry = 0;
951 1.1.1.2.10.1 bouyer ent.entry = *entry;
952 1.1.1.2.10.1 bouyer
953 1.1.1.2.10.1 bouyer /*
954 1.1.1.2.10.1 bouyer * If we're not logging then we can't recover-to-perform, so just
955 1.1.1.2.10.1 bouyer * perform.
956 1.1.1.2.10.1 bouyer */
957 1.1.1.2.10.1 bouyer if (strcmp(log_context->log_file, "/dev/null") == 0)
958 1.1.1.2.10.1 bouyer return context->db->hdb_store(context->context, context->db, 0, &ent);
959 1.1.1.2.10.1 bouyer
960 1.1.1.2.10.1 bouyer /*
961 1.1.1.2.10.1 bouyer * Test for any conflicting entries before writing the log. If we commit
962 1.1.1.2.10.1 bouyer * to the log we'll end-up rolling forward on recovery, but that would be
963 1.1.1.2.10.1 bouyer * wrong if the initial create is rejected.
964 1.1.1.2.10.1 bouyer */
965 1.1.1.2.10.1 bouyer ret = context->db->hdb_store(context->context, context->db,
966 1.1.1.2.10.1 bouyer HDB_F_PRECHECK, &ent);
967 1.1.1.2.10.1 bouyer if (ret == 0)
968 1.1.1.2.10.1 bouyer ret = hdb_entry2value(context->context, entry, &value);
969 1.1 elric if (ret)
970 1.1.1.2.10.1 bouyer return ret;
971 1.1.1.2.10.1 bouyer sp = krb5_storage_emem();
972 1.1.1.2.10.1 bouyer if (sp == NULL)
973 1.1.1.2.10.1 bouyer ret = ENOMEM;
974 1.1.1.2.10.1 bouyer if (ret == 0)
975 1.1.1.2.10.1 bouyer ret = kadm5_log_preamble(context, sp, kadm_create,
976 1.1.1.2.10.1 bouyer log_context->version + 1);
977 1.1.1.2.10.1 bouyer if (ret == 0)
978 1.1.1.2.10.1 bouyer ret = krb5_store_uint32(sp, value.length);
979 1.1.1.2.10.1 bouyer if (ret == 0) {
980 1.1.1.2.10.1 bouyer if (krb5_storage_write(sp, value.data, value.length) !=
981 1.1.1.2.10.1 bouyer (krb5_ssize_t)value.length)
982 1.1.1.2.10.1 bouyer ret = errno;
983 1.1.1.2.10.1 bouyer }
984 1.1.1.2.10.1 bouyer if (ret == 0)
985 1.1.1.2.10.1 bouyer ret = krb5_store_uint32(sp, value.length);
986 1.1.1.2.10.1 bouyer if (ret == 0)
987 1.1.1.2.10.1 bouyer ret = kadm5_log_postamble(log_context, sp,
988 1.1.1.2.10.1 bouyer log_context->version + 1);
989 1.1.1.2.10.1 bouyer if (ret == 0)
990 1.1.1.2.10.1 bouyer ret = kadm5_log_flush(context, sp);
991 1.1.1.2.10.1 bouyer krb5_storage_free(sp);
992 1.1.1.2.10.1 bouyer krb5_data_free(&value);
993 1.1.1.2.10.1 bouyer if (ret == 0)
994 1.1.1.2.10.1 bouyer ret = kadm5_log_recover(context, kadm_recover_commit);
995 1.1 elric return ret;
996 1.1 elric }
997 1.1 elric
998 1.1 elric /*
999 1.1 elric * Read the data of a create log record from `sp' and change the
1000 1.1 elric * database.
1001 1.1 elric */
1002 1.1 elric static kadm5_ret_t
1003 1.1.1.2.10.1 bouyer kadm5_log_replay_create(kadm5_server_context *context,
1004 1.1.1.2.10.1 bouyer uint32_t ver,
1005 1.1.1.2.10.1 bouyer uint32_t len,
1006 1.1.1.2.10.1 bouyer krb5_storage *sp)
1007 1.1 elric {
1008 1.1 elric krb5_error_code ret;
1009 1.1 elric krb5_data data;
1010 1.1 elric hdb_entry_ex ent;
1011 1.1 elric
1012 1.1 elric memset(&ent, 0, sizeof(ent));
1013 1.1 elric
1014 1.1.1.2.10.1 bouyer ret = krb5_data_alloc(&data, len);
1015 1.1 elric if (ret) {
1016 1.1 elric krb5_set_error_message(context->context, ret, "out of memory");
1017 1.1 elric return ret;
1018 1.1 elric }
1019 1.1.1.2.10.1 bouyer krb5_storage_read(sp, data.data, len);
1020 1.1.1.2.10.1 bouyer ret = hdb_value2entry(context->context, &data, &ent.entry);
1021 1.1 elric krb5_data_free(&data);
1022 1.1 elric if (ret) {
1023 1.1 elric krb5_set_error_message(context->context, ret,
1024 1.1.1.2.10.1 bouyer "Unmarshaling hdb entry in log failed, "
1025 1.1.1.2.10.1 bouyer "version: %ld", (long)ver);
1026 1.1 elric return ret;
1027 1.1 elric }
1028 1.1 elric ret = context->db->hdb_store(context->context, context->db, 0, &ent);
1029 1.1.1.2.10.1 bouyer hdb_free_entry(context->context, &ent);
1030 1.1 elric return ret;
1031 1.1 elric }
1032 1.1 elric
1033 1.1 elric /*
1034 1.1 elric * Add a `delete' operation to the log.
1035 1.1 elric */
1036 1.1 elric kadm5_ret_t
1037 1.1.1.2.10.1 bouyer kadm5_log_delete(kadm5_server_context *context,
1038 1.1.1.2.10.1 bouyer krb5_principal princ)
1039 1.1 elric {
1040 1.1 elric kadm5_ret_t ret;
1041 1.1 elric kadm5_log_context *log_context = &context->log_context;
1042 1.1.1.2.10.1 bouyer krb5_storage *sp;
1043 1.1.1.2.10.1 bouyer uint32_t len = 0; /* So dumb compilers don't warn */
1044 1.1.1.2.10.1 bouyer off_t end_off = 0; /* Ditto; this allows de-indentation by two levels */
1045 1.1.1.2.10.1 bouyer off_t off;
1046 1.1 elric
1047 1.1.1.2.10.1 bouyer if (strcmp(log_context->log_file, "/dev/null") == 0)
1048 1.1.1.2.10.1 bouyer return context->db->hdb_remove(context->context, context->db, 0,
1049 1.1.1.2.10.1 bouyer princ);
1050 1.1.1.2.10.1 bouyer ret = context->db->hdb_remove(context->context, context->db,
1051 1.1.1.2.10.1 bouyer HDB_F_PRECHECK, princ);
1052 1.1.1.2.10.1 bouyer if (ret)
1053 1.1.1.2.10.1 bouyer return ret;
1054 1.1 elric sp = krb5_storage_emem();
1055 1.1 elric if (sp == NULL)
1056 1.1.1.2.10.1 bouyer ret = ENOMEM;
1057 1.1.1.2.10.1 bouyer if (ret == 0)
1058 1.1.1.2.10.1 bouyer ret = kadm5_log_preamble(context, sp, kadm_delete,
1059 1.1.1.2.10.1 bouyer log_context->version + 1);
1060 1.1.1.2.10.1 bouyer if (ret) {
1061 1.1.1.2.10.1 bouyer krb5_storage_free(sp);
1062 1.1.1.2.10.1 bouyer return ret;
1063 1.1.1.2.10.1 bouyer }
1064 1.1.1.2.10.1 bouyer
1065 1.1.1.2.10.1 bouyer /*
1066 1.1.1.2.10.1 bouyer * Write a 0 length which we overwrite once we know the length of
1067 1.1.1.2.10.1 bouyer * the principal name payload.
1068 1.1.1.2.10.1 bouyer */
1069 1.1.1.2.10.1 bouyer off = krb5_storage_seek(sp, 0, SEEK_CUR);
1070 1.1.1.2.10.1 bouyer if (off == -1)
1071 1.1.1.2.10.1 bouyer ret = errno;
1072 1.1.1.2.10.1 bouyer if (ret == 0)
1073 1.1.1.2.10.1 bouyer ret = krb5_store_uint32(sp, 0);
1074 1.1.1.2.10.1 bouyer if (ret == 0)
1075 1.1.1.2.10.1 bouyer ret = krb5_store_principal(sp, princ);
1076 1.1.1.2.10.1 bouyer if (ret == 0) {
1077 1.1.1.2.10.1 bouyer end_off = krb5_storage_seek(sp, 0, SEEK_CUR);
1078 1.1.1.2.10.1 bouyer if (end_off == -1)
1079 1.1.1.2.10.1 bouyer ret = errno;
1080 1.1.1.2.10.1 bouyer else if (end_off < off)
1081 1.1.1.2.10.1 bouyer ret = KADM5_LOG_CORRUPT;
1082 1.1.1.2.10.1 bouyer }
1083 1.1.1.2.10.1 bouyer if (ret == 0) {
1084 1.1.1.2.10.1 bouyer /* We wrote sizeof(uint32_t) + payload length bytes */
1085 1.1.1.2.10.1 bouyer len = (uint32_t)(end_off - off);
1086 1.1.1.2.10.1 bouyer if (end_off - off != len || len < sizeof(len))
1087 1.1.1.2.10.1 bouyer ret = KADM5_LOG_CORRUPT;
1088 1.1.1.2.10.1 bouyer else
1089 1.1.1.2.10.1 bouyer len -= sizeof(len);
1090 1.1.1.2.10.1 bouyer }
1091 1.1.1.2.10.1 bouyer if (ret == 0 && krb5_storage_seek(sp, off, SEEK_SET) == -1)
1092 1.1.1.2.10.1 bouyer ret = errno;
1093 1.1.1.2.10.1 bouyer if (ret == 0)
1094 1.1.1.2.10.1 bouyer ret = krb5_store_uint32(sp, len);
1095 1.1.1.2.10.1 bouyer if (ret == 0 && krb5_storage_seek(sp, end_off, SEEK_SET) == -1)
1096 1.1.1.2.10.1 bouyer ret = errno;
1097 1.1.1.2.10.1 bouyer if (ret == 0)
1098 1.1.1.2.10.1 bouyer ret = krb5_store_uint32(sp, len);
1099 1.1.1.2.10.1 bouyer if (ret == 0)
1100 1.1.1.2.10.1 bouyer ret = kadm5_log_postamble(log_context, sp,
1101 1.1.1.2.10.1 bouyer log_context->version + 1);
1102 1.1.1.2.10.1 bouyer if (ret == 0)
1103 1.1.1.2.10.1 bouyer ret = kadm5_log_flush(context, sp);
1104 1.1.1.2.10.1 bouyer if (ret == 0)
1105 1.1.1.2.10.1 bouyer ret = kadm5_log_recover(context, kadm_recover_commit);
1106 1.1.1.2.10.1 bouyer krb5_storage_free(sp);
1107 1.1 elric return ret;
1108 1.1 elric }
1109 1.1 elric
1110 1.1 elric /*
1111 1.1 elric * Read a `delete' log operation from `sp' and apply it.
1112 1.1 elric */
1113 1.1 elric static kadm5_ret_t
1114 1.1.1.2.10.1 bouyer kadm5_log_replay_delete(kadm5_server_context *context,
1115 1.1.1.2.10.1 bouyer uint32_t ver, uint32_t len, krb5_storage *sp)
1116 1.1 elric {
1117 1.1 elric krb5_error_code ret;
1118 1.1 elric krb5_principal principal;
1119 1.1 elric
1120 1.1.1.2.10.1 bouyer ret = krb5_ret_principal(sp, &principal);
1121 1.1 elric if (ret) {
1122 1.1 elric krb5_set_error_message(context->context, ret, "Failed to read deleted "
1123 1.1 elric "principal from log version: %ld", (long)ver);
1124 1.1 elric return ret;
1125 1.1 elric }
1126 1.1 elric
1127 1.1.1.2.10.1 bouyer ret = context->db->hdb_remove(context->context, context->db, 0, principal);
1128 1.1.1.2.10.1 bouyer krb5_free_principal(context->context, principal);
1129 1.1 elric return ret;
1130 1.1 elric }
1131 1.1 elric
1132 1.1.1.2.10.1 bouyer static kadm5_ret_t kadm5_log_replay_rename(kadm5_server_context *,
1133 1.1.1.2.10.1 bouyer uint32_t, uint32_t,
1134 1.1.1.2.10.1 bouyer krb5_storage *);
1135 1.1.1.2.10.1 bouyer
1136 1.1 elric /*
1137 1.1 elric * Add a `rename' operation to the log.
1138 1.1 elric */
1139 1.1 elric kadm5_ret_t
1140 1.1.1.2.10.1 bouyer kadm5_log_rename(kadm5_server_context *context,
1141 1.1.1.2.10.1 bouyer krb5_principal source,
1142 1.1.1.2.10.1 bouyer hdb_entry *entry)
1143 1.1 elric {
1144 1.1 elric krb5_storage *sp;
1145 1.1 elric kadm5_ret_t ret;
1146 1.1.1.2.10.1 bouyer uint32_t len = 0; /* So dumb compilers don't warn */
1147 1.1.1.2.10.1 bouyer off_t end_off = 0; /* Ditto; this allows de-indentation by two levels */
1148 1.1 elric off_t off;
1149 1.1 elric krb5_data value;
1150 1.1.1.2.10.1 bouyer hdb_entry_ex ent;
1151 1.1 elric kadm5_log_context *log_context = &context->log_context;
1152 1.1 elric
1153 1.1.1.2.10.1 bouyer memset(&ent, 0, sizeof(ent));
1154 1.1.1.2.10.1 bouyer ent.ctx = 0;
1155 1.1.1.2.10.1 bouyer ent.free_entry = 0;
1156 1.1.1.2.10.1 bouyer ent.entry = *entry;
1157 1.1.1.2.10.1 bouyer
1158 1.1.1.2.10.1 bouyer if (strcmp(log_context->log_file, "/dev/null") == 0) {
1159 1.1.1.2.10.1 bouyer ret = context->db->hdb_store(context->context, context->db, 0, &ent);
1160 1.1.1.2.10.1 bouyer if (ret == 0)
1161 1.1.1.2.10.1 bouyer return context->db->hdb_remove(context->context, context->db, 0,
1162 1.1.1.2.10.1 bouyer source);
1163 1.1.1.2.10.1 bouyer return ret;
1164 1.1.1.2.10.1 bouyer }
1165 1.1 elric
1166 1.1.1.2.10.1 bouyer /*
1167 1.1.1.2.10.1 bouyer * Pre-check that the transaction will succeed.
1168 1.1.1.2.10.1 bouyer *
1169 1.1.1.2.10.1 bouyer * Note that rename doesn't work to swap a principal's canonical
1170 1.1.1.2.10.1 bouyer * name with one of its aliases. To make that work would require
1171 1.1.1.2.10.1 bouyer * adding an hdb_rename() method for renaming principals (there's an
1172 1.1.1.2.10.1 bouyer * hdb_rename() method already, but for renaming the HDB), which is
1173 1.1.1.2.10.1 bouyer * ETOOMUCHWORK for the time being.
1174 1.1.1.2.10.1 bouyer */
1175 1.1.1.2.10.1 bouyer ret = context->db->hdb_store(context->context, context->db,
1176 1.1.1.2.10.1 bouyer HDB_F_PRECHECK, &ent);
1177 1.1.1.2.10.1 bouyer if (ret == 0)
1178 1.1.1.2.10.1 bouyer ret = context->db->hdb_remove(context->context, context->db,
1179 1.1.1.2.10.1 bouyer HDB_F_PRECHECK, source);
1180 1.1 elric if (ret)
1181 1.1.1.2.10.1 bouyer return ret;
1182 1.1 elric
1183 1.1.1.2.10.1 bouyer sp = krb5_storage_emem();
1184 1.1.1.2.10.1 bouyer krb5_data_zero(&value);
1185 1.1.1.2.10.1 bouyer if (sp == NULL)
1186 1.1.1.2.10.1 bouyer ret = ENOMEM;
1187 1.1.1.2.10.1 bouyer if (ret == 0)
1188 1.1.1.2.10.1 bouyer ret = kadm5_log_preamble(context, sp, kadm_rename,
1189 1.1.1.2.10.1 bouyer log_context->version + 1);
1190 1.1.1.2.10.1 bouyer if (ret == 0)
1191 1.1.1.2.10.1 bouyer ret = hdb_entry2value(context->context, entry, &value);
1192 1.1.1.2.10.1 bouyer if (ret) {
1193 1.1.1.2.10.1 bouyer krb5_data_free(&value);
1194 1.1.1.2.10.1 bouyer krb5_storage_free(sp);
1195 1.1.1.2.10.1 bouyer return ret;
1196 1.1.1.2.10.1 bouyer }
1197 1.1 elric
1198 1.1.1.2.10.1 bouyer /*
1199 1.1.1.2.10.1 bouyer * Write a zero length which we'll overwrite once we know the length of the
1200 1.1.1.2.10.1 bouyer * payload.
1201 1.1.1.2.10.1 bouyer */
1202 1.1.1.2.10.1 bouyer off = krb5_storage_seek(sp, 0, SEEK_CUR);
1203 1.1.1.2.10.1 bouyer if (off == -1)
1204 1.1.1.2.10.1 bouyer ret = errno;
1205 1.1.1.2.10.1 bouyer if (ret == 0)
1206 1.1.1.2.10.1 bouyer ret = krb5_store_uint32(sp, 0);
1207 1.1.1.2.10.1 bouyer if (ret == 0)
1208 1.1.1.2.10.1 bouyer ret = krb5_store_principal(sp, source);
1209 1.1.1.2.10.1 bouyer if (ret == 0) {
1210 1.1.1.2.10.1 bouyer errno = 0;
1211 1.1.1.2.10.1 bouyer if (krb5_storage_write(sp, value.data, value.length) !=
1212 1.1.1.2.10.1 bouyer (krb5_ssize_t)value.length)
1213 1.1.1.2.10.1 bouyer ret = errno ? errno : EIO;
1214 1.1.1.2.10.1 bouyer }
1215 1.1.1.2.10.1 bouyer if (ret == 0) {
1216 1.1.1.2.10.1 bouyer end_off = krb5_storage_seek(sp, 0, SEEK_CUR);
1217 1.1.1.2.10.1 bouyer if (end_off == -1)
1218 1.1.1.2.10.1 bouyer ret = errno;
1219 1.1.1.2.10.1 bouyer else if (end_off < off)
1220 1.1.1.2.10.1 bouyer ret = KADM5_LOG_CORRUPT;
1221 1.1.1.2.10.1 bouyer }
1222 1.1.1.2.10.1 bouyer if (ret == 0) {
1223 1.1.1.2.10.1 bouyer /* We wrote sizeof(uint32_t) + payload length bytes */
1224 1.1.1.2.10.1 bouyer len = (uint32_t)(end_off - off);
1225 1.1.1.2.10.1 bouyer if (end_off - off != len || len < sizeof(len))
1226 1.1.1.2.10.1 bouyer ret = KADM5_LOG_CORRUPT;
1227 1.1.1.2.10.1 bouyer else
1228 1.1.1.2.10.1 bouyer len -= sizeof(len);
1229 1.1.1.2.10.1 bouyer if (ret == 0 && krb5_storage_seek(sp, off, SEEK_SET) == -1)
1230 1.1.1.2.10.1 bouyer ret = errno;
1231 1.1.1.2.10.1 bouyer if (ret == 0)
1232 1.1.1.2.10.1 bouyer ret = krb5_store_uint32(sp, len);
1233 1.1.1.2.10.1 bouyer if (ret == 0 && krb5_storage_seek(sp, end_off, SEEK_SET) == -1)
1234 1.1.1.2.10.1 bouyer ret = errno;
1235 1.1.1.2.10.1 bouyer if (ret == 0)
1236 1.1.1.2.10.1 bouyer ret = krb5_store_uint32(sp, len);
1237 1.1.1.2.10.1 bouyer if (ret == 0)
1238 1.1.1.2.10.1 bouyer ret = kadm5_log_postamble(log_context, sp,
1239 1.1.1.2.10.1 bouyer log_context->version + 1);
1240 1.1.1.2.10.1 bouyer if (ret == 0)
1241 1.1.1.2.10.1 bouyer ret = kadm5_log_flush(context, sp);
1242 1.1.1.2.10.1 bouyer if (ret == 0)
1243 1.1.1.2.10.1 bouyer ret = kadm5_log_recover(context, kadm_recover_commit);
1244 1.1.1.2.10.1 bouyer }
1245 1.1 elric krb5_data_free(&value);
1246 1.1 elric krb5_storage_free(sp);
1247 1.1 elric return ret;
1248 1.1 elric }
1249 1.1 elric
1250 1.1 elric /*
1251 1.1 elric * Read a `rename' log operation from `sp' and apply it.
1252 1.1 elric */
1253 1.1 elric
1254 1.1 elric static kadm5_ret_t
1255 1.1.1.2.10.1 bouyer kadm5_log_replay_rename(kadm5_server_context *context,
1256 1.1.1.2.10.1 bouyer uint32_t ver,
1257 1.1.1.2.10.1 bouyer uint32_t len,
1258 1.1.1.2.10.1 bouyer krb5_storage *sp)
1259 1.1 elric {
1260 1.1 elric krb5_error_code ret;
1261 1.1 elric krb5_principal source;
1262 1.1 elric hdb_entry_ex target_ent;
1263 1.1 elric krb5_data value;
1264 1.1 elric off_t off;
1265 1.1 elric size_t princ_len, data_len;
1266 1.1 elric
1267 1.1 elric memset(&target_ent, 0, sizeof(target_ent));
1268 1.1 elric
1269 1.1 elric off = krb5_storage_seek(sp, 0, SEEK_CUR);
1270 1.1.1.2.10.1 bouyer ret = krb5_ret_principal(sp, &source);
1271 1.1 elric if (ret) {
1272 1.1 elric krb5_set_error_message(context->context, ret, "Failed to read renamed "
1273 1.1 elric "principal in log, version: %ld", (long)ver);
1274 1.1 elric return ret;
1275 1.1 elric }
1276 1.1 elric princ_len = krb5_storage_seek(sp, 0, SEEK_CUR) - off;
1277 1.1 elric data_len = len - princ_len;
1278 1.1.1.2.10.1 bouyer ret = krb5_data_alloc(&value, data_len);
1279 1.1 elric if (ret) {
1280 1.1 elric krb5_free_principal (context->context, source);
1281 1.1 elric return ret;
1282 1.1 elric }
1283 1.1.1.2.10.1 bouyer krb5_storage_read(sp, value.data, data_len);
1284 1.1.1.2.10.1 bouyer ret = hdb_value2entry(context->context, &value, &target_ent.entry);
1285 1.1 elric krb5_data_free(&value);
1286 1.1 elric if (ret) {
1287 1.1.1.2.10.1 bouyer krb5_free_principal(context->context, source);
1288 1.1 elric return ret;
1289 1.1 elric }
1290 1.1.1.2.10.1 bouyer ret = context->db->hdb_store(context->context, context->db,
1291 1.1.1.2.10.1 bouyer 0, &target_ent);
1292 1.1.1.2.10.1 bouyer hdb_free_entry(context->context, &target_ent);
1293 1.1 elric if (ret) {
1294 1.1.1.2.10.1 bouyer krb5_free_principal(context->context, source);
1295 1.1 elric return ret;
1296 1.1 elric }
1297 1.1.1.2.10.1 bouyer ret = context->db->hdb_remove(context->context, context->db, 0, source);
1298 1.1.1.2.10.1 bouyer krb5_free_principal(context->context, source);
1299 1.1.1.2.10.1 bouyer
1300 1.1 elric return ret;
1301 1.1 elric }
1302 1.1 elric
1303 1.1 elric /*
1304 1.1 elric * Add a `modify' operation to the log.
1305 1.1 elric */
1306 1.1 elric kadm5_ret_t
1307 1.1.1.2.10.1 bouyer kadm5_log_modify(kadm5_server_context *context,
1308 1.1.1.2.10.1 bouyer hdb_entry *entry,
1309 1.1.1.2.10.1 bouyer uint32_t mask)
1310 1.1 elric {
1311 1.1 elric krb5_storage *sp;
1312 1.1 elric kadm5_ret_t ret;
1313 1.1 elric krb5_data value;
1314 1.1 elric uint32_t len;
1315 1.1.1.2.10.1 bouyer hdb_entry_ex ent;
1316 1.1 elric kadm5_log_context *log_context = &context->log_context;
1317 1.1 elric
1318 1.1.1.2.10.1 bouyer memset(&ent, 0, sizeof(ent));
1319 1.1.1.2.10.1 bouyer ent.ctx = 0;
1320 1.1.1.2.10.1 bouyer ent.free_entry = 0;
1321 1.1.1.2.10.1 bouyer ent.entry = *entry;
1322 1.1.1.2.10.1 bouyer
1323 1.1.1.2.10.1 bouyer if (strcmp(log_context->log_file, "/dev/null") == 0)
1324 1.1.1.2.10.1 bouyer return context->db->hdb_store(context->context, context->db,
1325 1.1.1.2.10.1 bouyer HDB_F_REPLACE, &ent);
1326 1.1 elric
1327 1.1.1.2.10.1 bouyer ret = context->db->hdb_store(context->context, context->db,
1328 1.1.1.2.10.1 bouyer HDB_F_PRECHECK | HDB_F_REPLACE, &ent);
1329 1.1 elric if (ret)
1330 1.1.1.2.10.1 bouyer return ret;
1331 1.1 elric
1332 1.1.1.2.10.1 bouyer sp = krb5_storage_emem();
1333 1.1.1.2.10.1 bouyer krb5_data_zero(&value);
1334 1.1.1.2.10.1 bouyer if (sp == NULL)
1335 1.1.1.2.10.1 bouyer ret = ENOMEM;
1336 1.1.1.2.10.1 bouyer if (ret == 0)
1337 1.1.1.2.10.1 bouyer ret = hdb_entry2value(context->context, entry, &value);
1338 1.1.1.2.10.1 bouyer if (ret) {
1339 1.1.1.2.10.1 bouyer krb5_data_free(&value);
1340 1.1.1.2.10.1 bouyer krb5_storage_free(sp);
1341 1.1.1.2.10.1 bouyer return ret;
1342 1.1.1.2.10.1 bouyer }
1343 1.1 elric
1344 1.1.1.2.10.1 bouyer len = value.length + sizeof(len);
1345 1.1.1.2.10.1 bouyer if (value.length > len || len > INT32_MAX)
1346 1.1.1.2.10.1 bouyer ret = E2BIG;
1347 1.1.1.2.10.1 bouyer if (ret == 0)
1348 1.1.1.2.10.1 bouyer ret = kadm5_log_preamble(context, sp, kadm_modify,
1349 1.1.1.2.10.1 bouyer log_context->version + 1);
1350 1.1.1.2.10.1 bouyer if (ret == 0)
1351 1.1.1.2.10.1 bouyer ret = krb5_store_uint32(sp, len);
1352 1.1.1.2.10.1 bouyer if (ret == 0)
1353 1.1.1.2.10.1 bouyer ret = krb5_store_uint32(sp, mask);
1354 1.1.1.2.10.1 bouyer if (ret == 0) {
1355 1.1.1.2.10.1 bouyer if (krb5_storage_write(sp, value.data, value.length) !=
1356 1.1.1.2.10.1 bouyer (krb5_ssize_t)value.length)
1357 1.1.1.2.10.1 bouyer ret = errno;
1358 1.1.1.2.10.1 bouyer }
1359 1.1.1.2.10.1 bouyer if (ret == 0)
1360 1.1.1.2.10.1 bouyer ret = krb5_store_uint32(sp, len);
1361 1.1.1.2.10.1 bouyer if (ret == 0)
1362 1.1.1.2.10.1 bouyer ret = kadm5_log_postamble(log_context, sp,
1363 1.1.1.2.10.1 bouyer log_context->version + 1);
1364 1.1.1.2.10.1 bouyer if (ret == 0)
1365 1.1.1.2.10.1 bouyer ret = kadm5_log_flush(context, sp);
1366 1.1.1.2.10.1 bouyer if (ret == 0)
1367 1.1.1.2.10.1 bouyer ret = kadm5_log_recover(context, kadm_recover_commit);
1368 1.1 elric krb5_data_free(&value);
1369 1.1 elric krb5_storage_free(sp);
1370 1.1 elric return ret;
1371 1.1 elric }
1372 1.1 elric
1373 1.1 elric /*
1374 1.1 elric * Read a `modify' log operation from `sp' and apply it.
1375 1.1 elric */
1376 1.1 elric static kadm5_ret_t
1377 1.1.1.2.10.1 bouyer kadm5_log_replay_modify(kadm5_server_context *context,
1378 1.1.1.2.10.1 bouyer uint32_t ver,
1379 1.1.1.2.10.1 bouyer uint32_t len,
1380 1.1.1.2.10.1 bouyer krb5_storage *sp)
1381 1.1 elric {
1382 1.1 elric krb5_error_code ret;
1383 1.1.1.2.10.1 bouyer uint32_t mask;
1384 1.1 elric krb5_data value;
1385 1.1 elric hdb_entry_ex ent, log_ent;
1386 1.1 elric
1387 1.1 elric memset(&log_ent, 0, sizeof(log_ent));
1388 1.1 elric
1389 1.1.1.2.10.1 bouyer ret = krb5_ret_uint32(sp, &mask);
1390 1.1.1.2.10.1 bouyer if (ret)
1391 1.1.1.2.10.1 bouyer return ret;
1392 1.1 elric len -= 4;
1393 1.1 elric ret = krb5_data_alloc (&value, len);
1394 1.1 elric if (ret) {
1395 1.1 elric krb5_set_error_message(context->context, ret, "out of memory");
1396 1.1 elric return ret;
1397 1.1 elric }
1398 1.1.1.2.10.1 bouyer errno = 0;
1399 1.1.1.2.10.1 bouyer if (krb5_storage_read (sp, value.data, len) != (krb5_ssize_t)len) {
1400 1.1.1.2.10.1 bouyer ret = errno ? errno : EIO;
1401 1.1.1.2.10.1 bouyer return ret;
1402 1.1.1.2.10.1 bouyer }
1403 1.1 elric ret = hdb_value2entry (context->context, &value, &log_ent.entry);
1404 1.1 elric krb5_data_free(&value);
1405 1.1 elric if (ret)
1406 1.1 elric return ret;
1407 1.1 elric
1408 1.1 elric memset(&ent, 0, sizeof(ent));
1409 1.1 elric ret = context->db->hdb_fetch_kvno(context->context, context->db,
1410 1.1 elric log_ent.entry.principal,
1411 1.1.1.2.10.1 bouyer HDB_F_DECRYPT|HDB_F_ALL_KVNOS|
1412 1.1.1.2.10.1 bouyer HDB_F_GET_ANY|HDB_F_ADMIN_DATA, 0, &ent);
1413 1.1 elric if (ret)
1414 1.1 elric goto out;
1415 1.1 elric if (mask & KADM5_PRINC_EXPIRE_TIME) {
1416 1.1 elric if (log_ent.entry.valid_end == NULL) {
1417 1.1 elric ent.entry.valid_end = NULL;
1418 1.1 elric } else {
1419 1.1 elric if (ent.entry.valid_end == NULL) {
1420 1.1 elric ent.entry.valid_end = malloc(sizeof(*ent.entry.valid_end));
1421 1.1 elric if (ent.entry.valid_end == NULL) {
1422 1.1 elric ret = ENOMEM;
1423 1.1 elric krb5_set_error_message(context->context, ret, "out of memory");
1424 1.1 elric goto out;
1425 1.1 elric }
1426 1.1 elric }
1427 1.1 elric *ent.entry.valid_end = *log_ent.entry.valid_end;
1428 1.1 elric }
1429 1.1 elric }
1430 1.1 elric if (mask & KADM5_PW_EXPIRATION) {
1431 1.1 elric if (log_ent.entry.pw_end == NULL) {
1432 1.1 elric ent.entry.pw_end = NULL;
1433 1.1 elric } else {
1434 1.1 elric if (ent.entry.pw_end == NULL) {
1435 1.1 elric ent.entry.pw_end = malloc(sizeof(*ent.entry.pw_end));
1436 1.1 elric if (ent.entry.pw_end == NULL) {
1437 1.1 elric ret = ENOMEM;
1438 1.1 elric krb5_set_error_message(context->context, ret, "out of memory");
1439 1.1 elric goto out;
1440 1.1 elric }
1441 1.1 elric }
1442 1.1 elric *ent.entry.pw_end = *log_ent.entry.pw_end;
1443 1.1 elric }
1444 1.1 elric }
1445 1.1 elric if (mask & KADM5_LAST_PWD_CHANGE) {
1446 1.1.1.2.10.1 bouyer krb5_warnx (context->context,
1447 1.1.1.2.10.1 bouyer "Unimplemented mask KADM5_LAST_PWD_CHANGE");
1448 1.1 elric }
1449 1.1 elric if (mask & KADM5_ATTRIBUTES) {
1450 1.1 elric ent.entry.flags = log_ent.entry.flags;
1451 1.1 elric }
1452 1.1 elric if (mask & KADM5_MAX_LIFE) {
1453 1.1 elric if (log_ent.entry.max_life == NULL) {
1454 1.1 elric ent.entry.max_life = NULL;
1455 1.1 elric } else {
1456 1.1 elric if (ent.entry.max_life == NULL) {
1457 1.1 elric ent.entry.max_life = malloc (sizeof(*ent.entry.max_life));
1458 1.1 elric if (ent.entry.max_life == NULL) {
1459 1.1 elric ret = ENOMEM;
1460 1.1 elric krb5_set_error_message(context->context, ret, "out of memory");
1461 1.1 elric goto out;
1462 1.1 elric }
1463 1.1 elric }
1464 1.1 elric *ent.entry.max_life = *log_ent.entry.max_life;
1465 1.1 elric }
1466 1.1 elric }
1467 1.1 elric if ((mask & KADM5_MOD_TIME) && (mask & KADM5_MOD_NAME)) {
1468 1.1 elric if (ent.entry.modified_by == NULL) {
1469 1.1 elric ent.entry.modified_by = malloc(sizeof(*ent.entry.modified_by));
1470 1.1 elric if (ent.entry.modified_by == NULL) {
1471 1.1 elric ret = ENOMEM;
1472 1.1 elric krb5_set_error_message(context->context, ret, "out of memory");
1473 1.1 elric goto out;
1474 1.1 elric }
1475 1.1 elric } else
1476 1.1 elric free_Event(ent.entry.modified_by);
1477 1.1 elric ret = copy_Event(log_ent.entry.modified_by, ent.entry.modified_by);
1478 1.1 elric if (ret) {
1479 1.1 elric krb5_set_error_message(context->context, ret, "out of memory");
1480 1.1 elric goto out;
1481 1.1 elric }
1482 1.1 elric }
1483 1.1 elric if (mask & KADM5_KVNO) {
1484 1.1 elric ent.entry.kvno = log_ent.entry.kvno;
1485 1.1 elric }
1486 1.1 elric if (mask & KADM5_MKVNO) {
1487 1.1.1.2.10.1 bouyer krb5_warnx(context->context, "Unimplemented mask KADM5_KVNO");
1488 1.1 elric }
1489 1.1 elric if (mask & KADM5_AUX_ATTRIBUTES) {
1490 1.1.1.2.10.1 bouyer krb5_warnx(context->context,
1491 1.1.1.2.10.1 bouyer "Unimplemented mask KADM5_AUX_ATTRIBUTES");
1492 1.1 elric }
1493 1.1 elric if (mask & KADM5_POLICY_CLR) {
1494 1.1.1.2.10.1 bouyer krb5_warnx(context->context, "Unimplemented mask KADM5_POLICY_CLR");
1495 1.1 elric }
1496 1.1 elric if (mask & KADM5_MAX_RLIFE) {
1497 1.1 elric if (log_ent.entry.max_renew == NULL) {
1498 1.1 elric ent.entry.max_renew = NULL;
1499 1.1 elric } else {
1500 1.1 elric if (ent.entry.max_renew == NULL) {
1501 1.1 elric ent.entry.max_renew = malloc (sizeof(*ent.entry.max_renew));
1502 1.1 elric if (ent.entry.max_renew == NULL) {
1503 1.1 elric ret = ENOMEM;
1504 1.1 elric krb5_set_error_message(context->context, ret, "out of memory");
1505 1.1 elric goto out;
1506 1.1 elric }
1507 1.1 elric }
1508 1.1 elric *ent.entry.max_renew = *log_ent.entry.max_renew;
1509 1.1 elric }
1510 1.1 elric }
1511 1.1 elric if (mask & KADM5_LAST_SUCCESS) {
1512 1.1.1.2.10.1 bouyer krb5_warnx(context->context, "Unimplemented mask KADM5_LAST_SUCCESS");
1513 1.1 elric }
1514 1.1 elric if (mask & KADM5_LAST_FAILED) {
1515 1.1.1.2.10.1 bouyer krb5_warnx(context->context, "Unimplemented mask KADM5_LAST_FAILED");
1516 1.1 elric }
1517 1.1 elric if (mask & KADM5_FAIL_AUTH_COUNT) {
1518 1.1.1.2.10.1 bouyer krb5_warnx(context->context,
1519 1.1.1.2.10.1 bouyer "Unimplemented mask KADM5_FAIL_AUTH_COUNT");
1520 1.1 elric }
1521 1.1 elric if (mask & KADM5_KEY_DATA) {
1522 1.1 elric size_t num;
1523 1.1.1.2 pettai size_t i;
1524 1.1 elric
1525 1.1.1.2.10.1 bouyer /*
1526 1.1.1.2.10.1 bouyer * We don't need to do anything about key history here because
1527 1.1.1.2.10.1 bouyer * the log entry contains a complete entry, including hdb
1528 1.1.1.2.10.1 bouyer * extensions. We do need to make sure that KADM5_TL_DATA is in
1529 1.1.1.2.10.1 bouyer * the mask though, since that's what it takes to update the
1530 1.1.1.2.10.1 bouyer * extensions (see below).
1531 1.1.1.2.10.1 bouyer */
1532 1.1.1.2.10.1 bouyer mask |= KADM5_TL_DATA;
1533 1.1.1.2.10.1 bouyer
1534 1.1 elric for (i = 0; i < ent.entry.keys.len; ++i)
1535 1.1 elric free_Key(&ent.entry.keys.val[i]);
1536 1.1 elric free (ent.entry.keys.val);
1537 1.1 elric
1538 1.1 elric num = log_ent.entry.keys.len;
1539 1.1 elric
1540 1.1 elric ent.entry.keys.len = num;
1541 1.1 elric ent.entry.keys.val = malloc(len * sizeof(*ent.entry.keys.val));
1542 1.1 elric if (ent.entry.keys.val == NULL) {
1543 1.1 elric krb5_set_error_message(context->context, ENOMEM, "out of memory");
1544 1.1.1.2.10.1 bouyer ret = ENOMEM;
1545 1.1.1.2.10.1 bouyer goto out;
1546 1.1 elric }
1547 1.1 elric for (i = 0; i < ent.entry.keys.len; ++i) {
1548 1.1 elric ret = copy_Key(&log_ent.entry.keys.val[i],
1549 1.1 elric &ent.entry.keys.val[i]);
1550 1.1 elric if (ret) {
1551 1.1 elric krb5_set_error_message(context->context, ret, "out of memory");
1552 1.1 elric goto out;
1553 1.1 elric }
1554 1.1 elric }
1555 1.1 elric }
1556 1.1 elric if ((mask & KADM5_TL_DATA) && log_ent.entry.extensions) {
1557 1.1 elric HDB_extensions *es = ent.entry.extensions;
1558 1.1 elric
1559 1.1 elric ent.entry.extensions = calloc(1, sizeof(*ent.entry.extensions));
1560 1.1 elric if (ent.entry.extensions == NULL)
1561 1.1 elric goto out;
1562 1.1 elric
1563 1.1 elric ret = copy_HDB_extensions(log_ent.entry.extensions,
1564 1.1 elric ent.entry.extensions);
1565 1.1 elric if (ret) {
1566 1.1 elric krb5_set_error_message(context->context, ret, "out of memory");
1567 1.1 elric free(ent.entry.extensions);
1568 1.1 elric ent.entry.extensions = es;
1569 1.1 elric goto out;
1570 1.1 elric }
1571 1.1 elric if (es) {
1572 1.1 elric free_HDB_extensions(es);
1573 1.1 elric free(es);
1574 1.1 elric }
1575 1.1 elric }
1576 1.1 elric ret = context->db->hdb_store(context->context, context->db,
1577 1.1 elric HDB_F_REPLACE, &ent);
1578 1.1 elric out:
1579 1.1.1.2.10.1 bouyer hdb_free_entry(context->context, &ent);
1580 1.1.1.2.10.1 bouyer hdb_free_entry(context->context, &log_ent);
1581 1.1 elric return ret;
1582 1.1 elric }
1583 1.1 elric
1584 1.1 elric /*
1585 1.1.1.2.10.1 bouyer * Update the first entry (which should be a `nop'), the "uber-entry".
1586 1.1 elric */
1587 1.1.1.2.10.1 bouyer static kadm5_ret_t
1588 1.1.1.2.10.1 bouyer log_update_uber(kadm5_server_context *context, off_t off)
1589 1.1.1.2.10.1 bouyer {
1590 1.1.1.2.10.1 bouyer kadm5_log_context *log_context = &context->log_context;
1591 1.1.1.2.10.1 bouyer kadm5_ret_t ret = 0;
1592 1.1.1.2.10.1 bouyer krb5_storage *sp, *mem_sp;
1593 1.1.1.2.10.1 bouyer krb5_data data;
1594 1.1.1.2.10.1 bouyer uint32_t op, len;
1595 1.1.1.2.10.1 bouyer ssize_t bytes;
1596 1.1.1.2.10.1 bouyer
1597 1.1.1.2.10.1 bouyer if (strcmp(log_context->log_file, "/dev/null") == 0)
1598 1.1.1.2.10.1 bouyer return 0;
1599 1.1.1.2.10.1 bouyer
1600 1.1.1.2.10.1 bouyer if (log_context->read_only)
1601 1.1.1.2.10.1 bouyer return EROFS;
1602 1.1.1.2.10.1 bouyer
1603 1.1.1.2.10.1 bouyer krb5_data_zero(&data);
1604 1.1.1.2.10.1 bouyer
1605 1.1.1.2.10.1 bouyer mem_sp = krb5_storage_emem();
1606 1.1.1.2.10.1 bouyer if (mem_sp == NULL)
1607 1.1.1.2.10.1 bouyer return ENOMEM;
1608 1.1.1.2.10.1 bouyer
1609 1.1.1.2.10.1 bouyer sp = krb5_storage_from_fd(log_context->log_fd);
1610 1.1.1.2.10.1 bouyer if (sp == NULL) {
1611 1.1.1.2.10.1 bouyer krb5_storage_free(mem_sp);
1612 1.1.1.2.10.1 bouyer return ENOMEM;
1613 1.1.1.2.10.1 bouyer }
1614 1.1.1.2.10.1 bouyer
1615 1.1.1.2.10.1 bouyer /* Skip first entry's version and timestamp */
1616 1.1.1.2.10.1 bouyer if (krb5_storage_seek(sp, 8, SEEK_SET) == -1) {
1617 1.1.1.2.10.1 bouyer ret = errno;
1618 1.1.1.2.10.1 bouyer goto out;
1619 1.1.1.2.10.1 bouyer }
1620 1.1.1.2.10.1 bouyer
1621 1.1.1.2.10.1 bouyer /* If the first entry is not a nop, there's nothing we can do here */
1622 1.1.1.2.10.1 bouyer ret = krb5_ret_uint32(sp, &op);
1623 1.1.1.2.10.1 bouyer if (ret || op != kadm_nop)
1624 1.1.1.2.10.1 bouyer goto out;
1625 1.1.1.2.10.1 bouyer
1626 1.1.1.2.10.1 bouyer /* If the first entry is not a 16-byte nop, ditto */
1627 1.1.1.2.10.1 bouyer ret = krb5_ret_uint32(sp, &len);
1628 1.1.1.2.10.1 bouyer if (ret || len != LOG_UBER_LEN)
1629 1.1.1.2.10.1 bouyer goto out;
1630 1.1.1.2.10.1 bouyer
1631 1.1.1.2.10.1 bouyer /*
1632 1.1.1.2.10.1 bouyer * Try to make the writes here as close to atomic as possible: a
1633 1.1.1.2.10.1 bouyer * single write() call.
1634 1.1.1.2.10.1 bouyer */
1635 1.1.1.2.10.1 bouyer ret = krb5_store_uint64(mem_sp, off);
1636 1.1.1.2.10.1 bouyer if (ret)
1637 1.1.1.2.10.1 bouyer goto out;
1638 1.1.1.2.10.1 bouyer ret = krb5_store_uint32(mem_sp, log_context->last_time);
1639 1.1.1.2.10.1 bouyer if (ret)
1640 1.1.1.2.10.1 bouyer goto out;
1641 1.1.1.2.10.1 bouyer ret = krb5_store_uint32(mem_sp, log_context->version);
1642 1.1.1.2.10.1 bouyer if (ret)
1643 1.1.1.2.10.1 bouyer goto out;
1644 1.1.1.2.10.1 bouyer
1645 1.1.1.2.10.1 bouyer krb5_storage_to_data(mem_sp, &data);
1646 1.1.1.2.10.1 bouyer bytes = krb5_storage_write(sp, data.data, data.length);
1647 1.1.1.2.10.1 bouyer if (bytes < 0)
1648 1.1.1.2.10.1 bouyer ret = errno;
1649 1.1.1.2.10.1 bouyer else if (bytes != data.length)
1650 1.1.1.2.10.1 bouyer ret = EIO;
1651 1.1.1.2.10.1 bouyer
1652 1.1.1.2.10.1 bouyer /*
1653 1.1.1.2.10.1 bouyer * We don't fsync() this write because we can recover if the write
1654 1.1.1.2.10.1 bouyer * doesn't complete, though for now we don't have code for properly
1655 1.1.1.2.10.1 bouyer * dealing with the offset not getting written completely.
1656 1.1.1.2.10.1 bouyer *
1657 1.1.1.2.10.1 bouyer * We should probably have two copies of the offset so we can use
1658 1.1.1.2.10.1 bouyer * one copy to verify the other, and when they don't match we could
1659 1.1.1.2.10.1 bouyer * traverse the whole log forwards, replaying just the last entry.
1660 1.1.1.2.10.1 bouyer */
1661 1.1.1.2.10.1 bouyer
1662 1.1.1.2.10.1 bouyer out:
1663 1.1.1.2.10.1 bouyer if (ret == 0)
1664 1.1.1.2.10.1 bouyer kadm5_log_signal_master(context);
1665 1.1.1.2.10.1 bouyer krb5_data_free(&data);
1666 1.1.1.2.10.1 bouyer krb5_storage_free(sp);
1667 1.1.1.2.10.1 bouyer krb5_storage_free(mem_sp);
1668 1.1.1.2.10.1 bouyer if (lseek(log_context->log_fd, off, SEEK_SET) == -1)
1669 1.1.1.2.10.1 bouyer ret = ret ? ret : errno;
1670 1.1.1.2.10.1 bouyer
1671 1.1.1.2.10.1 bouyer return ret;
1672 1.1.1.2.10.1 bouyer }
1673 1.1 elric
1674 1.1.1.2.10.1 bouyer /*
1675 1.1.1.2.10.1 bouyer * Add a `nop' operation to the log. Does not close the log.
1676 1.1.1.2.10.1 bouyer */
1677 1.1 elric kadm5_ret_t
1678 1.1.1.2.10.1 bouyer kadm5_log_nop(kadm5_server_context *context, enum kadm_nop_type nop_type)
1679 1.1 elric {
1680 1.1 elric krb5_storage *sp;
1681 1.1 elric kadm5_ret_t ret;
1682 1.1 elric kadm5_log_context *log_context = &context->log_context;
1683 1.1.1.2.10.1 bouyer off_t off;
1684 1.1.1.2.10.1 bouyer uint32_t vno = log_context->version;
1685 1.1.1.2.10.1 bouyer
1686 1.1.1.2.10.1 bouyer if (strcmp(log_context->log_file, "/dev/null") == 0)
1687 1.1.1.2.10.1 bouyer return 0;
1688 1.1.1.2.10.1 bouyer
1689 1.1.1.2.10.1 bouyer off = lseek(log_context->log_fd, 0, SEEK_CUR);
1690 1.1.1.2.10.1 bouyer if (off == -1)
1691 1.1.1.2.10.1 bouyer return errno;
1692 1.1 elric
1693 1.1 elric sp = krb5_storage_emem();
1694 1.1.1.2.10.1 bouyer ret = kadm5_log_preamble(context, sp, kadm_nop, off == 0 ? 0 : vno + 1);
1695 1.1.1.2.10.1 bouyer if (ret)
1696 1.1.1.2.10.1 bouyer goto out;
1697 1.1.1.2.10.1 bouyer
1698 1.1.1.2.10.1 bouyer if (off == 0) {
1699 1.1.1.2.10.1 bouyer /*
1700 1.1.1.2.10.1 bouyer * First entry (uber-entry) gets room for offset of next new
1701 1.1.1.2.10.1 bouyer * entry and time and version of last entry.
1702 1.1.1.2.10.1 bouyer */
1703 1.1.1.2.10.1 bouyer ret = krb5_store_uint32(sp, LOG_UBER_LEN);
1704 1.1.1.2.10.1 bouyer /* These get overwritten with the same values below */
1705 1.1.1.2.10.1 bouyer if (ret == 0)
1706 1.1.1.2.10.1 bouyer ret = krb5_store_uint64(sp, LOG_UBER_SZ);
1707 1.1.1.2.10.1 bouyer if (ret == 0)
1708 1.1.1.2.10.1 bouyer ret = krb5_store_uint32(sp, log_context->last_time);
1709 1.1.1.2.10.1 bouyer if (ret == 0)
1710 1.1.1.2.10.1 bouyer ret = krb5_store_uint32(sp, vno);
1711 1.1.1.2.10.1 bouyer if (ret == 0)
1712 1.1.1.2.10.1 bouyer ret = krb5_store_uint32(sp, LOG_UBER_LEN);
1713 1.1.1.2.10.1 bouyer } else if (nop_type == kadm_nop_plain) {
1714 1.1.1.2.10.1 bouyer ret = krb5_store_uint32(sp, 0);
1715 1.1.1.2.10.1 bouyer if (ret == 0)
1716 1.1.1.2.10.1 bouyer ret = krb5_store_uint32(sp, 0);
1717 1.1.1.2.10.1 bouyer } else {
1718 1.1.1.2.10.1 bouyer ret = krb5_store_uint32(sp, sizeof(uint32_t));
1719 1.1.1.2.10.1 bouyer if (ret == 0)
1720 1.1.1.2.10.1 bouyer ret = krb5_store_uint32(sp, nop_type);
1721 1.1.1.2.10.1 bouyer if (ret == 0)
1722 1.1.1.2.10.1 bouyer ret = krb5_store_uint32(sp, sizeof(uint32_t));
1723 1.1.1.2.10.1 bouyer }
1724 1.1.1.2.10.1 bouyer
1725 1.1.1.2.10.1 bouyer if (ret == 0)
1726 1.1.1.2.10.1 bouyer ret = kadm5_log_postamble(log_context, sp, off == 0 ? 0 : vno + 1);
1727 1.1.1.2.10.1 bouyer if (ret == 0)
1728 1.1.1.2.10.1 bouyer ret = kadm5_log_flush(context, sp);
1729 1.1 elric
1730 1.1.1.2.10.1 bouyer if (ret == 0 && off == 0 && nop_type != kadm_nop_plain)
1731 1.1.1.2.10.1 bouyer ret = kadm5_log_nop(context, nop_type);
1732 1.1.1.2.10.1 bouyer
1733 1.1.1.2.10.1 bouyer if (ret == 0 && off != 0)
1734 1.1.1.2.10.1 bouyer ret = kadm5_log_recover(context, kadm_recover_commit);
1735 1.1.1.2.10.1 bouyer
1736 1.1.1.2.10.1 bouyer out:
1737 1.1.1.2.10.1 bouyer krb5_storage_free(sp);
1738 1.1 elric return ret;
1739 1.1 elric }
1740 1.1 elric
1741 1.1 elric /*
1742 1.1.1.2.10.1 bouyer * Read a `nop' log operation from `sp' and "apply" it (there's nothing
1743 1.1.1.2.10.1 bouyer * to do).
1744 1.1.1.2.10.1 bouyer *
1745 1.1.1.2.10.1 bouyer * FIXME Actually, if the nop payload is 4 bytes and contains an enum
1746 1.1.1.2.10.1 bouyer * kadm_nop_type value of kadm_nop_trunc then we should truncate the
1747 1.1.1.2.10.1 bouyer * log, and if it contains a kadm_nop_close then we should rename a new
1748 1.1.1.2.10.1 bouyer * log into place. However, this is not implemented yet.
1749 1.1 elric */
1750 1.1 elric static kadm5_ret_t
1751 1.1.1.2.10.1 bouyer kadm5_log_replay_nop(kadm5_server_context *context,
1752 1.1.1.2.10.1 bouyer uint32_t ver,
1753 1.1.1.2.10.1 bouyer uint32_t len,
1754 1.1.1.2.10.1 bouyer krb5_storage *sp)
1755 1.1 elric {
1756 1.1 elric return 0;
1757 1.1 elric }
1758 1.1 elric
1759 1.1.1.2.10.1 bouyer struct replay_cb_data {
1760 1.1.1.2.10.1 bouyer size_t count;
1761 1.1.1.2.10.1 bouyer uint32_t ver;
1762 1.1.1.2.10.1 bouyer enum kadm_recover_mode mode;
1763 1.1.1.2.10.1 bouyer };
1764 1.1.1.2.10.1 bouyer
1765 1.1.1.2.10.1 bouyer
1766 1.1 elric /*
1767 1.1.1.2.10.1 bouyer * Recover or perform the initial commit of an unconfirmed log entry
1768 1.1 elric */
1769 1.1.1.2.10.1 bouyer static kadm5_ret_t
1770 1.1.1.2.10.1 bouyer recover_replay(kadm5_server_context *context,
1771 1.1.1.2.10.1 bouyer uint32_t ver, time_t timestamp, enum kadm_ops op,
1772 1.1.1.2.10.1 bouyer uint32_t len, krb5_storage *sp, void *ctx)
1773 1.1.1.2.10.1 bouyer {
1774 1.1.1.2.10.1 bouyer struct replay_cb_data *data = ctx;
1775 1.1.1.2.10.1 bouyer kadm5_ret_t ret;
1776 1.1.1.2.10.1 bouyer off_t off;
1777 1.1.1.2.10.1 bouyer
1778 1.1.1.2.10.1 bouyer /* On initial commit there must be just one pending unconfirmed entry */
1779 1.1.1.2.10.1 bouyer if (data->count > 0 && data->mode == kadm_recover_commit)
1780 1.1.1.2.10.1 bouyer return KADM5_LOG_CORRUPT;
1781 1.1.1.2.10.1 bouyer
1782 1.1.1.2.10.1 bouyer /* We're at the start of the payload; compute end of entry offset */
1783 1.1.1.2.10.1 bouyer off = krb5_storage_seek(sp, 0, SEEK_CUR) + len + LOG_TRAILER_SZ;
1784 1.1.1.2.10.1 bouyer
1785 1.1.1.2.10.1 bouyer /* We cannot perform log recovery on LDAP and such backends */
1786 1.1.1.2.10.1 bouyer if (data->mode == kadm_recover_replay &&
1787 1.1.1.2.10.1 bouyer (context->db->hdb_capability_flags & HDB_CAP_F_SHARED_DIRECTORY))
1788 1.1.1.2.10.1 bouyer ret = 0;
1789 1.1.1.2.10.1 bouyer else
1790 1.1.1.2.10.1 bouyer ret = kadm5_log_replay(context, op, ver, len, sp);
1791 1.1.1.2.10.1 bouyer switch (ret) {
1792 1.1.1.2.10.1 bouyer case HDB_ERR_NOENTRY:
1793 1.1.1.2.10.1 bouyer case HDB_ERR_EXISTS:
1794 1.1.1.2.10.1 bouyer if (data->mode != kadm_recover_replay)
1795 1.1.1.2.10.1 bouyer return ret;
1796 1.1.1.2.10.1 bouyer case 0:
1797 1.1.1.2.10.1 bouyer break;
1798 1.1.1.2.10.1 bouyer case KADM5_LOG_CORRUPT:
1799 1.1.1.2.10.1 bouyer return -1;
1800 1.1.1.2.10.1 bouyer default:
1801 1.1.1.2.10.1 bouyer krb5_warn(context->context, ret, "unexpected error while replaying");
1802 1.1.1.2.10.1 bouyer return -1;
1803 1.1.1.2.10.1 bouyer }
1804 1.1.1.2.10.1 bouyer data->count++;
1805 1.1.1.2.10.1 bouyer data->ver = ver;
1806 1.1.1.2.10.1 bouyer
1807 1.1.1.2.10.1 bouyer /*
1808 1.1.1.2.10.1 bouyer * With replay we may be making multiple HDB changes. We must sync the
1809 1.1.1.2.10.1 bouyer * confirmation of each one before moving on to the next. Otherwise, we
1810 1.1.1.2.10.1 bouyer * might attempt to replay multiple already applied updates, and this may
1811 1.1.1.2.10.1 bouyer * introduce unintended intermediate states or fail to yield the same final
1812 1.1.1.2.10.1 bouyer * result.
1813 1.1.1.2.10.1 bouyer */
1814 1.1.1.2.10.1 bouyer kadm5_log_set_version(context, ver);
1815 1.1.1.2.10.1 bouyer ret = log_update_uber(context, off);
1816 1.1.1.2.10.1 bouyer if (ret == 0 && data->mode != kadm_recover_commit)
1817 1.1.1.2.10.1 bouyer ret = krb5_storage_fsync(sp);
1818 1.1.1.2.10.1 bouyer return ret;
1819 1.1.1.2.10.1 bouyer }
1820 1.1 elric
1821 1.1.1.2.10.1 bouyer
1822 1.1.1.2.10.1 bouyer kadm5_ret_t
1823 1.1.1.2.10.1 bouyer kadm5_log_recover(kadm5_server_context *context, enum kadm_recover_mode mode)
1824 1.1.1.2.10.1 bouyer {
1825 1.1.1.2.10.1 bouyer kadm5_ret_t ret;
1826 1.1.1.2.10.1 bouyer krb5_storage *sp;
1827 1.1.1.2.10.1 bouyer struct replay_cb_data replay_data;
1828 1.1.1.2.10.1 bouyer
1829 1.1.1.2.10.1 bouyer replay_data.count = 0;
1830 1.1.1.2.10.1 bouyer replay_data.ver = 0;
1831 1.1.1.2.10.1 bouyer replay_data.mode = mode;
1832 1.1.1.2.10.1 bouyer
1833 1.1.1.2.10.1 bouyer sp = kadm5_log_goto_end(context, context->log_context.log_fd);
1834 1.1.1.2.10.1 bouyer if (sp == NULL)
1835 1.1.1.2.10.1 bouyer return errno ? errno : EIO;
1836 1.1.1.2.10.1 bouyer
1837 1.1.1.2.10.1 bouyer ret = kadm5_log_foreach(context, kadm_forward | kadm_unconfirmed,
1838 1.1.1.2.10.1 bouyer NULL, recover_replay, &replay_data);
1839 1.1.1.2.10.1 bouyer if (ret == 0 && mode == kadm_recover_commit && replay_data.count != 1)
1840 1.1.1.2.10.1 bouyer ret = KADM5_LOG_CORRUPT;
1841 1.1.1.2.10.1 bouyer krb5_storage_free(sp);
1842 1.1.1.2.10.1 bouyer return ret;
1843 1.1.1.2.10.1 bouyer }
1844 1.1.1.2.10.1 bouyer
1845 1.1.1.2.10.1 bouyer /*
1846 1.1.1.2.10.1 bouyer * Call `func' for each log record in the log in `context'.
1847 1.1.1.2.10.1 bouyer *
1848 1.1.1.2.10.1 bouyer * `func' is optional.
1849 1.1.1.2.10.1 bouyer *
1850 1.1.1.2.10.1 bouyer * If `func' returns -1 then log traversal terminates and this returns 0.
1851 1.1.1.2.10.1 bouyer * Otherwise `func''s return is returned if there are no other errors.
1852 1.1.1.2.10.1 bouyer */
1853 1.1 elric kadm5_ret_t
1854 1.1.1.2.10.1 bouyer kadm5_log_foreach(kadm5_server_context *context,
1855 1.1.1.2.10.1 bouyer enum kadm_iter_opts iter_opts,
1856 1.1.1.2.10.1 bouyer off_t *off_lastp,
1857 1.1.1.2.10.1 bouyer kadm5_ret_t (*func)(kadm5_server_context *server_context,
1858 1.1.1.2.10.1 bouyer uint32_t ver, time_t timestamp,
1859 1.1.1.2.10.1 bouyer enum kadm_ops op, uint32_t len,
1860 1.1.1.2.10.1 bouyer krb5_storage *sp, void *ctx),
1861 1.1.1.2.10.1 bouyer void *ctx)
1862 1.1 elric {
1863 1.1.1.2.10.1 bouyer kadm5_ret_t ret = 0;
1864 1.1 elric int fd = context->log_context.log_fd;
1865 1.1 elric krb5_storage *sp;
1866 1.1.1.2.10.1 bouyer off_t off_last;
1867 1.1.1.2.10.1 bouyer off_t this_entry = 0;
1868 1.1.1.2.10.1 bouyer off_t log_end = 0;
1869 1.1.1.2.10.1 bouyer
1870 1.1.1.2.10.1 bouyer if (strcmp(context->log_context.log_file, "/dev/null") == 0)
1871 1.1.1.2.10.1 bouyer return 0;
1872 1.1.1.2.10.1 bouyer
1873 1.1.1.2.10.1 bouyer if (off_lastp == NULL)
1874 1.1.1.2.10.1 bouyer off_lastp = &off_last;
1875 1.1.1.2.10.1 bouyer *off_lastp = -1;
1876 1.1.1.2.10.1 bouyer
1877 1.1.1.2.10.1 bouyer if (((iter_opts & kadm_forward) && (iter_opts & kadm_backward)) ||
1878 1.1.1.2.10.1 bouyer (!(iter_opts & kadm_confirmed) && !(iter_opts & kadm_unconfirmed)))
1879 1.1.1.2.10.1 bouyer return EINVAL;
1880 1.1.1.2.10.1 bouyer
1881 1.1.1.2.10.1 bouyer if ((iter_opts & kadm_forward) && (iter_opts & kadm_confirmed) &&
1882 1.1.1.2.10.1 bouyer (iter_opts & kadm_unconfirmed)) {
1883 1.1.1.2.10.1 bouyer /*
1884 1.1.1.2.10.1 bouyer * We want to traverse all log entries, confirmed or not, from
1885 1.1.1.2.10.1 bouyer * the start, then there's no need to kadm5_log_goto_end()
1886 1.1.1.2.10.1 bouyer * -- no reason to try to find the end.
1887 1.1.1.2.10.1 bouyer */
1888 1.1.1.2.10.1 bouyer sp = krb5_storage_from_fd(fd);
1889 1.1.1.2.10.1 bouyer if (sp == NULL)
1890 1.1.1.2.10.1 bouyer return errno;
1891 1.1.1.2.10.1 bouyer
1892 1.1.1.2.10.1 bouyer log_end = krb5_storage_seek(sp, 0, SEEK_END);
1893 1.1.1.2.10.1 bouyer if (log_end == -1 ||
1894 1.1.1.2.10.1 bouyer krb5_storage_seek(sp, 0, SEEK_SET) == -1) {
1895 1.1.1.2.10.1 bouyer ret = errno;
1896 1.1.1.2.10.1 bouyer krb5_storage_free(sp);
1897 1.1.1.2.10.1 bouyer return ret;
1898 1.1.1.2.10.1 bouyer }
1899 1.1.1.2.10.1 bouyer } else {
1900 1.1.1.2.10.1 bouyer /* Get the end of the log based on the uber entry */
1901 1.1.1.2.10.1 bouyer sp = kadm5_log_goto_end(context, fd);
1902 1.1.1.2.10.1 bouyer if (sp == NULL)
1903 1.1.1.2.10.1 bouyer return errno;
1904 1.1.1.2.10.1 bouyer log_end = krb5_storage_seek(sp, 0, SEEK_CUR);
1905 1.1.1.2.10.1 bouyer }
1906 1.1.1.2.10.1 bouyer
1907 1.1.1.2.10.1 bouyer *off_lastp = log_end;
1908 1.1.1.2.10.1 bouyer
1909 1.1.1.2.10.1 bouyer if ((iter_opts & kadm_forward) && (iter_opts & kadm_confirmed)) {
1910 1.1.1.2.10.1 bouyer /* Start at the beginning */
1911 1.1.1.2.10.1 bouyer if (krb5_storage_seek(sp, 0, SEEK_SET) == -1) {
1912 1.1.1.2.10.1 bouyer ret = errno;
1913 1.1.1.2.10.1 bouyer krb5_storage_free(sp);
1914 1.1.1.2.10.1 bouyer return ret;
1915 1.1.1.2.10.1 bouyer }
1916 1.1.1.2.10.1 bouyer } else if ((iter_opts & kadm_backward) && (iter_opts & kadm_unconfirmed)) {
1917 1.1.1.2.10.1 bouyer /*
1918 1.1.1.2.10.1 bouyer * We're at the confirmed end but need to be at the unconfirmed
1919 1.1.1.2.10.1 bouyer * end. Skip forward to the real end, re-entering to do it.
1920 1.1.1.2.10.1 bouyer */
1921 1.1.1.2.10.1 bouyer ret = kadm5_log_foreach(context, kadm_forward | kadm_unconfirmed,
1922 1.1.1.2.10.1 bouyer &log_end, NULL, NULL);
1923 1.1.1.2.10.1 bouyer if (ret)
1924 1.1.1.2.10.1 bouyer return ret;
1925 1.1.1.2.10.1 bouyer if (krb5_storage_seek(sp, log_end, SEEK_SET) == -1) {
1926 1.1.1.2.10.1 bouyer ret = errno;
1927 1.1.1.2.10.1 bouyer krb5_storage_free(sp);
1928 1.1.1.2.10.1 bouyer return ret;
1929 1.1.1.2.10.1 bouyer }
1930 1.1.1.2.10.1 bouyer }
1931 1.1 elric
1932 1.1 elric for (;;) {
1933 1.1.1.2.10.1 bouyer uint32_t ver, ver2, len, len2;
1934 1.1.1.2.10.1 bouyer uint32_t tstamp;
1935 1.1.1.2.10.1 bouyer time_t timestamp;
1936 1.1.1.2.10.1 bouyer enum kadm_ops op;
1937 1.1.1.2.10.1 bouyer
1938 1.1.1.2.10.1 bouyer if ((iter_opts & kadm_backward)) {
1939 1.1.1.2.10.1 bouyer off_t o;
1940 1.1.1.2.10.1 bouyer
1941 1.1.1.2.10.1 bouyer o = krb5_storage_seek(sp, 0, SEEK_CUR);
1942 1.1.1.2.10.1 bouyer if (o == 0 ||
1943 1.1.1.2.10.1 bouyer ((iter_opts & kadm_unconfirmed) && o <= *off_lastp))
1944 1.1.1.2.10.1 bouyer break;
1945 1.1.1.2.10.1 bouyer ret = kadm5_log_previous(context->context, sp, &ver,
1946 1.1.1.2.10.1 bouyer ×tamp, &op, &len);
1947 1.1.1.2.10.1 bouyer if (ret)
1948 1.1.1.2.10.1 bouyer break;
1949 1.1.1.2.10.1 bouyer
1950 1.1.1.2.10.1 bouyer /* Offset is now at payload of current entry */
1951 1.1.1.2.10.1 bouyer
1952 1.1.1.2.10.1 bouyer o = krb5_storage_seek(sp, 0, SEEK_CUR);
1953 1.1.1.2.10.1 bouyer if (o == -1) {
1954 1.1.1.2.10.1 bouyer ret = errno;
1955 1.1.1.2.10.1 bouyer break;
1956 1.1.1.2.10.1 bouyer }
1957 1.1.1.2.10.1 bouyer this_entry = o - LOG_HEADER_SZ;
1958 1.1.1.2.10.1 bouyer if (this_entry < 0) {
1959 1.1.1.2.10.1 bouyer ret = KADM5_LOG_CORRUPT;
1960 1.1.1.2.10.1 bouyer break;
1961 1.1.1.2.10.1 bouyer }
1962 1.1.1.2.10.1 bouyer } else {
1963 1.1.1.2.10.1 bouyer /* Offset is now at start of current entry, read header */
1964 1.1.1.2.10.1 bouyer this_entry = krb5_storage_seek(sp, 0, SEEK_CUR);
1965 1.1.1.2.10.1 bouyer if (!(iter_opts & kadm_unconfirmed) && this_entry == log_end)
1966 1.1.1.2.10.1 bouyer break;
1967 1.1.1.2.10.1 bouyer ret = get_header(sp, LOG_NOPEEK, &ver, &tstamp, &op, &len);
1968 1.1.1.2.10.1 bouyer if (ret == HEIM_ERR_EOF) {
1969 1.1.1.2.10.1 bouyer ret = 0;
1970 1.1.1.2.10.1 bouyer break;
1971 1.1.1.2.10.1 bouyer }
1972 1.1.1.2.10.1 bouyer timestamp = tstamp;
1973 1.1.1.2.10.1 bouyer if (ret)
1974 1.1.1.2.10.1 bouyer break;
1975 1.1.1.2.10.1 bouyer /* Offset is now at payload of current entry */
1976 1.1.1.2.10.1 bouyer }
1977 1.1.1.2.10.1 bouyer
1978 1.1.1.2.10.1 bouyer /* Validate trailer before calling the callback */
1979 1.1.1.2.10.1 bouyer if (krb5_storage_seek(sp, len, SEEK_CUR) == -1) {
1980 1.1.1.2.10.1 bouyer ret = errno;
1981 1.1.1.2.10.1 bouyer break;
1982 1.1.1.2.10.1 bouyer }
1983 1.1.1.2.10.1 bouyer
1984 1.1.1.2.10.1 bouyer ret = krb5_ret_uint32(sp, &len2);
1985 1.1.1.2.10.1 bouyer if (ret)
1986 1.1.1.2.10.1 bouyer break;
1987 1.1.1.2.10.1 bouyer ret = krb5_ret_uint32(sp, &ver2);
1988 1.1.1.2.10.1 bouyer if (ret)
1989 1.1.1.2.10.1 bouyer break;
1990 1.1.1.2.10.1 bouyer if (len != len2 || ver != ver2) {
1991 1.1.1.2.10.1 bouyer ret = KADM5_LOG_CORRUPT;
1992 1.1 elric break;
1993 1.1.1.2.10.1 bouyer }
1994 1.1.1.2.10.1 bouyer
1995 1.1.1.2.10.1 bouyer /* Rewind to start of payload and call callback if we have one */
1996 1.1.1.2.10.1 bouyer if (krb5_storage_seek(sp, this_entry + LOG_HEADER_SZ,
1997 1.1.1.2.10.1 bouyer SEEK_SET) == -1) {
1998 1.1.1.2.10.1 bouyer ret = errno;
1999 1.1.1.2.10.1 bouyer break;
2000 1.1.1.2.10.1 bouyer }
2001 1.1.1.2.10.1 bouyer
2002 1.1.1.2.10.1 bouyer if (func != NULL) {
2003 1.1.1.2.10.1 bouyer ret = (*func)(context, ver, timestamp, op, len, sp, ctx);
2004 1.1.1.2.10.1 bouyer if (ret) {
2005 1.1.1.2.10.1 bouyer /* Callback signals desire to stop by returning -1 */
2006 1.1.1.2.10.1 bouyer if (ret == -1)
2007 1.1.1.2.10.1 bouyer ret = 0;
2008 1.1.1.2.10.1 bouyer break;
2009 1.1.1.2.10.1 bouyer }
2010 1.1.1.2.10.1 bouyer }
2011 1.1.1.2.10.1 bouyer if ((iter_opts & kadm_forward)) {
2012 1.1.1.2.10.1 bouyer off_t o;
2013 1.1.1.2.10.1 bouyer
2014 1.1.1.2.10.1 bouyer o = krb5_storage_seek(sp, this_entry+LOG_WRAPPER_SZ+len, SEEK_SET);
2015 1.1.1.2.10.1 bouyer if (o == -1) {
2016 1.1.1.2.10.1 bouyer ret = errno;
2017 1.1.1.2.10.1 bouyer break;
2018 1.1.1.2.10.1 bouyer }
2019 1.1.1.2.10.1 bouyer if (o > log_end)
2020 1.1.1.2.10.1 bouyer *off_lastp = o;
2021 1.1.1.2.10.1 bouyer } else if ((iter_opts & kadm_backward)) {
2022 1.1.1.2.10.1 bouyer /*
2023 1.1.1.2.10.1 bouyer * Rewind to the start of this entry so kadm5_log_previous()
2024 1.1.1.2.10.1 bouyer * can find the previous one.
2025 1.1.1.2.10.1 bouyer */
2026 1.1.1.2.10.1 bouyer if (krb5_storage_seek(sp, this_entry, SEEK_SET) == -1) {
2027 1.1.1.2.10.1 bouyer ret = errno;
2028 1.1.1.2.10.1 bouyer break;
2029 1.1.1.2.10.1 bouyer }
2030 1.1.1.2.10.1 bouyer }
2031 1.1.1.2.10.1 bouyer }
2032 1.1.1.2.10.1 bouyer if ((ret == HEIM_ERR_EOF || ret == KADM5_LOG_CORRUPT) &&
2033 1.1.1.2.10.1 bouyer (iter_opts & kadm_forward) &&
2034 1.1.1.2.10.1 bouyer context->log_context.lock_mode == LOCK_EX) {
2035 1.1.1.2.10.1 bouyer /*
2036 1.1.1.2.10.1 bouyer * Truncate partially written last log entry so we can write
2037 1.1.1.2.10.1 bouyer * again.
2038 1.1.1.2.10.1 bouyer */
2039 1.1.1.2.10.1 bouyer ret = krb5_storage_truncate(sp, this_entry);
2040 1.1.1.2.10.1 bouyer if (ret == 0 &&
2041 1.1.1.2.10.1 bouyer krb5_storage_seek(sp, this_entry, SEEK_SET) == -1)
2042 1.1.1.2.10.1 bouyer ret = errno;
2043 1.1.1.2.10.1 bouyer krb5_warnx(context->context, "Truncating log at partial or "
2044 1.1.1.2.10.1 bouyer "corrupt %s entry",
2045 1.1.1.2.10.1 bouyer this_entry > log_end ? "unconfirmed" : "confirmed");
2046 1.1 elric }
2047 1.1 elric krb5_storage_free(sp);
2048 1.1.1.2.10.1 bouyer return ret;
2049 1.1.1.2.10.1 bouyer }
2050 1.1.1.2.10.1 bouyer
2051 1.1.1.2.10.1 bouyer /*
2052 1.1.1.2.10.1 bouyer * Go to the second record, which, if we have an uber record, will be
2053 1.1.1.2.10.1 bouyer * the first record.
2054 1.1.1.2.10.1 bouyer */
2055 1.1.1.2.10.1 bouyer static krb5_storage *
2056 1.1.1.2.10.1 bouyer log_goto_first(kadm5_server_context *server_context, int fd)
2057 1.1.1.2.10.1 bouyer {
2058 1.1.1.2.10.1 bouyer krb5_storage *sp;
2059 1.1.1.2.10.1 bouyer enum kadm_ops op;
2060 1.1.1.2.10.1 bouyer uint32_t ver, len;
2061 1.1.1.2.10.1 bouyer kadm5_ret_t ret;
2062 1.1.1.2.10.1 bouyer
2063 1.1.1.2.10.1 bouyer if (fd == -1) {
2064 1.1.1.2.10.1 bouyer errno = EINVAL;
2065 1.1.1.2.10.1 bouyer return NULL;
2066 1.1.1.2.10.1 bouyer }
2067 1.1.1.2.10.1 bouyer
2068 1.1.1.2.10.1 bouyer sp = krb5_storage_from_fd(fd);
2069 1.1.1.2.10.1 bouyer if (sp == NULL)
2070 1.1.1.2.10.1 bouyer return NULL;
2071 1.1.1.2.10.1 bouyer
2072 1.1.1.2.10.1 bouyer if (krb5_storage_seek(sp, 0, SEEK_SET) == -1)
2073 1.1.1.2.10.1 bouyer return NULL;
2074 1.1.1.2.10.1 bouyer
2075 1.1.1.2.10.1 bouyer ret = get_header(sp, LOG_DOPEEK, &ver, NULL, &op, &len);
2076 1.1.1.2.10.1 bouyer if (ret) {
2077 1.1.1.2.10.1 bouyer krb5_storage_free(sp);
2078 1.1.1.2.10.1 bouyer errno = ret;
2079 1.1.1.2.10.1 bouyer return NULL;
2080 1.1.1.2.10.1 bouyer }
2081 1.1.1.2.10.1 bouyer if (op == kadm_nop && len == LOG_UBER_LEN && seek_next(sp) == -1) {
2082 1.1.1.2.10.1 bouyer krb5_storage_free(sp);
2083 1.1.1.2.10.1 bouyer return NULL;
2084 1.1.1.2.10.1 bouyer }
2085 1.1.1.2.10.1 bouyer return sp;
2086 1.1 elric }
2087 1.1 elric
2088 1.1 elric /*
2089 1.1 elric * Go to end of log.
2090 1.1.1.2.10.1 bouyer *
2091 1.1.1.2.10.1 bouyer * XXX This really needs to return a kadm5_ret_t and either output a
2092 1.1.1.2.10.1 bouyer * krb5_storage * via an argument, or take one as input.
2093 1.1 elric */
2094 1.1 elric
2095 1.1 elric krb5_storage *
2096 1.1.1.2.10.1 bouyer kadm5_log_goto_end(kadm5_server_context *server_context, int fd)
2097 1.1 elric {
2098 1.1.1.2.10.1 bouyer krb5_error_code ret = 0;
2099 1.1 elric krb5_storage *sp;
2100 1.1.1.2.10.1 bouyer enum kadm_ops op;
2101 1.1.1.2.10.1 bouyer uint32_t ver, len;
2102 1.1.1.2.10.1 bouyer uint32_t tstamp;
2103 1.1.1.2.10.1 bouyer uint64_t off;
2104 1.1.1.2.10.1 bouyer
2105 1.1.1.2.10.1 bouyer if (fd == -1) {
2106 1.1.1.2.10.1 bouyer errno = EINVAL;
2107 1.1.1.2.10.1 bouyer return NULL;
2108 1.1.1.2.10.1 bouyer }
2109 1.1.1.2.10.1 bouyer
2110 1.1.1.2.10.1 bouyer sp = krb5_storage_from_fd(fd);
2111 1.1.1.2.10.1 bouyer if (sp == NULL)
2112 1.1.1.2.10.1 bouyer return NULL;
2113 1.1 elric
2114 1.1.1.2.10.1 bouyer if (krb5_storage_seek(sp, 0, SEEK_SET) == -1) {
2115 1.1.1.2.10.1 bouyer ret = errno;
2116 1.1.1.2.10.1 bouyer goto fail;
2117 1.1.1.2.10.1 bouyer }
2118 1.1.1.2.10.1 bouyer ret = get_header(sp, LOG_NOPEEK, &ver, &tstamp, &op, &len);
2119 1.1.1.2.10.1 bouyer if (ret == HEIM_ERR_EOF) {
2120 1.1.1.2.10.1 bouyer (void) krb5_storage_seek(sp, 0, SEEK_SET);
2121 1.1.1.2.10.1 bouyer return sp;
2122 1.1.1.2.10.1 bouyer }
2123 1.1.1.2.10.1 bouyer if (ret == KADM5_LOG_CORRUPT)
2124 1.1.1.2.10.1 bouyer goto truncate;
2125 1.1.1.2.10.1 bouyer if (ret)
2126 1.1.1.2.10.1 bouyer goto fail;
2127 1.1.1.2.10.1 bouyer
2128 1.1.1.2.10.1 bouyer if (op == kadm_nop && len == LOG_UBER_LEN) {
2129 1.1.1.2.10.1 bouyer /* New style log */
2130 1.1.1.2.10.1 bouyer ret = krb5_ret_uint64(sp, &off);
2131 1.1.1.2.10.1 bouyer if (ret)
2132 1.1.1.2.10.1 bouyer goto truncate;
2133 1.1.1.2.10.1 bouyer
2134 1.1.1.2.10.1 bouyer if (krb5_storage_seek(sp, off, SEEK_SET) == -1)
2135 1.1.1.2.10.1 bouyer goto fail;
2136 1.1.1.2.10.1 bouyer
2137 1.1.1.2.10.1 bouyer if (off >= LOG_UBER_SZ) {
2138 1.1.1.2.10.1 bouyer ret = get_version_prev(sp, &ver, NULL);
2139 1.1.1.2.10.1 bouyer if (ret == 0)
2140 1.1.1.2.10.1 bouyer return sp;
2141 1.1.1.2.10.1 bouyer }
2142 1.1.1.2.10.1 bouyer /* Invalid offset in uber entry */
2143 1.1.1.2.10.1 bouyer goto truncate;
2144 1.1.1.2.10.1 bouyer }
2145 1.1.1.2.10.1 bouyer
2146 1.1.1.2.10.1 bouyer /* Old log with no uber entry */
2147 1.1.1.2.10.1 bouyer if (krb5_storage_seek(sp, 0, SEEK_END) == -1) {
2148 1.1.1.2.10.1 bouyer static int warned = 0;
2149 1.1.1.2.10.1 bouyer if (!warned) {
2150 1.1.1.2.10.1 bouyer warned = 1;
2151 1.1.1.2.10.1 bouyer krb5_warnx(server_context->context,
2152 1.1.1.2.10.1 bouyer "Old log found; truncate it to upgrade");
2153 1.1.1.2.10.1 bouyer }
2154 1.1.1.2.10.1 bouyer }
2155 1.1.1.2.10.1 bouyer ret = get_version_prev(sp, &ver, NULL);
2156 1.1.1.2.10.1 bouyer if (ret)
2157 1.1.1.2.10.1 bouyer goto truncate;
2158 1.1 elric return sp;
2159 1.1.1.2.10.1 bouyer
2160 1.1.1.2.10.1 bouyer truncate:
2161 1.1.1.2.10.1 bouyer /* If we can, truncate */
2162 1.1.1.2.10.1 bouyer if (server_context->log_context.lock_mode == LOCK_EX) {
2163 1.1.1.2.10.1 bouyer ret = kadm5_log_reinit(server_context, 0);
2164 1.1.1.2.10.1 bouyer if (ret == 0) {
2165 1.1.1.2.10.1 bouyer krb5_warn(server_context->context, ret,
2166 1.1.1.2.10.1 bouyer "Invalid log; truncating to recover");
2167 1.1.1.2.10.1 bouyer if (krb5_storage_seek(sp, 0, SEEK_END) == -1)
2168 1.1.1.2.10.1 bouyer return NULL;
2169 1.1.1.2.10.1 bouyer return sp;
2170 1.1.1.2.10.1 bouyer }
2171 1.1.1.2.10.1 bouyer }
2172 1.1.1.2.10.1 bouyer krb5_warn(server_context->context, ret,
2173 1.1.1.2.10.1 bouyer "Invalid log; truncate to recover");
2174 1.1.1.2.10.1 bouyer
2175 1.1.1.2.10.1 bouyer fail:
2176 1.1.1.2.10.1 bouyer errno = ret;
2177 1.1.1.2.10.1 bouyer krb5_storage_free(sp);
2178 1.1.1.2.10.1 bouyer return NULL;
2179 1.1 elric }
2180 1.1 elric
2181 1.1 elric /*
2182 1.1 elric * Return previous log entry.
2183 1.1 elric *
2184 1.1.1.2.10.1 bouyer * The pointer in `sp' is assumed to be at the top of the entry after
2185 1.1.1.2.10.1 bouyer * previous entry (e.g., at EOF). On success, the `sp' pointer is set to
2186 1.1.1.2.10.1 bouyer * data portion of previous entry. In case of error, it's not changed
2187 1.1.1.2.10.1 bouyer * at all.
2188 1.1 elric */
2189 1.1 elric kadm5_ret_t
2190 1.1.1.2.10.1 bouyer kadm5_log_previous(krb5_context context,
2191 1.1.1.2.10.1 bouyer krb5_storage *sp,
2192 1.1.1.2.10.1 bouyer uint32_t *verp,
2193 1.1.1.2.10.1 bouyer time_t *tstampp,
2194 1.1.1.2.10.1 bouyer enum kadm_ops *opp,
2195 1.1.1.2.10.1 bouyer uint32_t *lenp)
2196 1.1 elric {
2197 1.1 elric krb5_error_code ret;
2198 1.1.1.2.10.1 bouyer off_t oldoff;
2199 1.1.1.2.10.1 bouyer uint32_t ver2, len2;
2200 1.1.1.2.10.1 bouyer uint32_t tstamp;
2201 1.1 elric
2202 1.1 elric oldoff = krb5_storage_seek(sp, 0, SEEK_CUR);
2203 1.1.1.2.10.1 bouyer if (oldoff == -1)
2204 1.1.1.2.10.1 bouyer goto log_corrupt;
2205 1.1 elric
2206 1.1.1.2.10.1 bouyer /* This reads the physical version of the uber record */
2207 1.1.1.2.10.1 bouyer if (seek_prev(sp, verp, lenp) == -1)
2208 1.1.1.2.10.1 bouyer goto log_corrupt;
2209 1.1.1.2.10.1 bouyer
2210 1.1.1.2.10.1 bouyer ret = get_header(sp, LOG_NOPEEK, &ver2, &tstamp, opp, &len2);
2211 1.1.1.2.10.1 bouyer if (ret) {
2212 1.1.1.2.10.1 bouyer (void) krb5_storage_seek(sp, oldoff, SEEK_SET);
2213 1.1.1.2.10.1 bouyer return ret;
2214 1.1 elric }
2215 1.1.1.2.10.1 bouyer if (tstampp)
2216 1.1.1.2.10.1 bouyer *tstampp = tstamp;
2217 1.1.1.2.10.1 bouyer if (ver2 != *verp || len2 != *lenp)
2218 1.1.1.2.10.1 bouyer goto log_corrupt;
2219 1.1.1.2.10.1 bouyer
2220 1.1 elric return 0;
2221 1.1 elric
2222 1.1.1.2.10.1 bouyer log_corrupt:
2223 1.1.1.2.10.1 bouyer (void) krb5_storage_seek(sp, oldoff, SEEK_SET);
2224 1.1.1.2.10.1 bouyer return KADM5_LOG_CORRUPT;
2225 1.1 elric }
2226 1.1 elric
2227 1.1 elric /*
2228 1.1 elric * Replay a record from the log
2229 1.1 elric */
2230 1.1 elric
2231 1.1 elric kadm5_ret_t
2232 1.1.1.2.10.1 bouyer kadm5_log_replay(kadm5_server_context *context,
2233 1.1.1.2.10.1 bouyer enum kadm_ops op,
2234 1.1.1.2.10.1 bouyer uint32_t ver,
2235 1.1.1.2.10.1 bouyer uint32_t len,
2236 1.1.1.2.10.1 bouyer krb5_storage *sp)
2237 1.1 elric {
2238 1.1 elric switch (op) {
2239 1.1 elric case kadm_create :
2240 1.1.1.2.10.1 bouyer return kadm5_log_replay_create(context, ver, len, sp);
2241 1.1 elric case kadm_delete :
2242 1.1.1.2.10.1 bouyer return kadm5_log_replay_delete(context, ver, len, sp);
2243 1.1 elric case kadm_rename :
2244 1.1.1.2.10.1 bouyer return kadm5_log_replay_rename(context, ver, len, sp);
2245 1.1 elric case kadm_modify :
2246 1.1.1.2.10.1 bouyer return kadm5_log_replay_modify(context, ver, len, sp);
2247 1.1 elric case kadm_nop :
2248 1.1.1.2.10.1 bouyer return kadm5_log_replay_nop(context, ver, len, sp);
2249 1.1 elric default :
2250 1.1.1.2.10.1 bouyer /*
2251 1.1.1.2.10.1 bouyer * FIXME This default arm makes it difficult to add new kadm_ops
2252 1.1.1.2.10.1 bouyer * values.
2253 1.1.1.2.10.1 bouyer */
2254 1.1 elric krb5_set_error_message(context->context, KADM5_FAILURE,
2255 1.1 elric "Unsupported replay op %d", (int)op);
2256 1.1.1.2.10.1 bouyer (void) krb5_storage_seek(sp, len, SEEK_CUR);
2257 1.1 elric return KADM5_FAILURE;
2258 1.1 elric }
2259 1.1 elric }
2260 1.1 elric
2261 1.1.1.2.10.1 bouyer struct load_entries_data {
2262 1.1.1.2.10.1 bouyer krb5_data *entries;
2263 1.1.1.2.10.1 bouyer unsigned char *p;
2264 1.1.1.2.10.1 bouyer uint32_t first;
2265 1.1.1.2.10.1 bouyer uint32_t last;
2266 1.1.1.2.10.1 bouyer size_t bytes;
2267 1.1.1.2.10.1 bouyer size_t nentries;
2268 1.1.1.2.10.1 bouyer size_t maxbytes;
2269 1.1.1.2.10.1 bouyer size_t maxentries;
2270 1.1.1.2.10.1 bouyer };
2271 1.1.1.2.10.1 bouyer
2272 1.1.1.2.10.1 bouyer
2273 1.1 elric /*
2274 1.1.1.2.10.1 bouyer * Prepend one entry with header and trailer to the entry buffer, stopping when
2275 1.1.1.2.10.1 bouyer * we've reached either of the byte or entry-count limits (if non-zero).
2276 1.1.1.2.10.1 bouyer *
2277 1.1.1.2.10.1 bouyer * This is a two-pass algorithm:
2278 1.1.1.2.10.1 bouyer *
2279 1.1.1.2.10.1 bouyer * In the first pass, when entries->entries == NULL, we compute the space
2280 1.1.1.2.10.1 bouyer * required, and count the entries that fit up from zero.
2281 1.1.1.2.10.1 bouyer *
2282 1.1.1.2.10.1 bouyer * In the second pass we fill the buffer, and count the entries back down to
2283 1.1.1.2.10.1 bouyer * zero. The space used must be an exact fit, and the number of entries must
2284 1.1.1.2.10.1 bouyer * reach zero at that point or an error is returned.
2285 1.1.1.2.10.1 bouyer *
2286 1.1.1.2.10.1 bouyer * The caller MUST check that entries->nentries == 0 at the end of the second
2287 1.1.1.2.10.1 bouyer * pass.
2288 1.1 elric */
2289 1.1.1.2.10.1 bouyer static kadm5_ret_t
2290 1.1.1.2.10.1 bouyer load_entries_cb(kadm5_server_context *server_context,
2291 1.1.1.2.10.1 bouyer uint32_t ver,
2292 1.1.1.2.10.1 bouyer time_t timestamp,
2293 1.1.1.2.10.1 bouyer enum kadm_ops op,
2294 1.1.1.2.10.1 bouyer uint32_t len,
2295 1.1.1.2.10.1 bouyer krb5_storage *sp,
2296 1.1.1.2.10.1 bouyer void *ctx)
2297 1.1.1.2.10.1 bouyer {
2298 1.1.1.2.10.1 bouyer struct load_entries_data *entries = ctx;
2299 1.1.1.2.10.1 bouyer kadm5_ret_t ret;
2300 1.1.1.2.10.1 bouyer ssize_t bytes;
2301 1.1.1.2.10.1 bouyer size_t entry_len = len + LOG_WRAPPER_SZ;
2302 1.1.1.2.10.1 bouyer unsigned char *base;
2303 1.1.1.2.10.1 bouyer
2304 1.1.1.2.10.1 bouyer if (entries->entries == NULL) {
2305 1.1.1.2.10.1 bouyer size_t total = entries->bytes + entry_len;
2306 1.1.1.2.10.1 bouyer
2307 1.1.1.2.10.1 bouyer /*
2308 1.1.1.2.10.1 bouyer * First run: find the size of krb5_data buffer needed.
2309 1.1.1.2.10.1 bouyer *
2310 1.1.1.2.10.1 bouyer * If the log was huge we'd have to perhaps open a temp file for this.
2311 1.1.1.2.10.1 bouyer * For now KISS.
2312 1.1.1.2.10.1 bouyer */
2313 1.1.1.2.10.1 bouyer if ((op == kadm_nop && entry_len == LOG_UBER_SZ) ||
2314 1.1.1.2.10.1 bouyer entry_len < len /*overflow?*/ ||
2315 1.1.1.2.10.1 bouyer (entries->maxbytes > 0 && total > entries->maxbytes) ||
2316 1.1.1.2.10.1 bouyer total < entries->bytes /*overflow?*/ ||
2317 1.1.1.2.10.1 bouyer (entries->maxentries > 0 && entries->nentries == entries->maxentries))
2318 1.1.1.2.10.1 bouyer return -1; /* stop iteration */
2319 1.1.1.2.10.1 bouyer entries->bytes = total;
2320 1.1.1.2.10.1 bouyer entries->first = ver;
2321 1.1.1.2.10.1 bouyer if (entries->nentries++ == 0)
2322 1.1.1.2.10.1 bouyer entries->last = ver;
2323 1.1.1.2.10.1 bouyer return 0;
2324 1.1.1.2.10.1 bouyer }
2325 1.1.1.2.10.1 bouyer
2326 1.1.1.2.10.1 bouyer /* Second run: load the data into memory */
2327 1.1.1.2.10.1 bouyer base = (unsigned char *)entries->entries->data;
2328 1.1.1.2.10.1 bouyer if (entries->p - base < entry_len && entries->p != base) {
2329 1.1.1.2.10.1 bouyer /*
2330 1.1.1.2.10.1 bouyer * This can't happen normally: we stop the log record iteration
2331 1.1.1.2.10.1 bouyer * above before we get here. This could happen if someone wrote
2332 1.1.1.2.10.1 bouyer * garbage to the log while we were traversing it. We return an
2333 1.1.1.2.10.1 bouyer * error instead of asserting.
2334 1.1.1.2.10.1 bouyer */
2335 1.1.1.2.10.1 bouyer return KADM5_LOG_CORRUPT;
2336 1.1.1.2.10.1 bouyer }
2337 1.1 elric
2338 1.1.1.2.10.1 bouyer /*
2339 1.1.1.2.10.1 bouyer * sp here is a krb5_storage_from_fd() of the log file, and the
2340 1.1.1.2.10.1 bouyer * offset pointer points at the current log record payload.
2341 1.1.1.2.10.1 bouyer *
2342 1.1.1.2.10.1 bouyer * Seek back to the start of the record poayload so we can read the
2343 1.1.1.2.10.1 bouyer * whole record.
2344 1.1.1.2.10.1 bouyer */
2345 1.1.1.2.10.1 bouyer if (krb5_storage_seek(sp, -LOG_HEADER_SZ, SEEK_CUR) == -1)
2346 1.1.1.2.10.1 bouyer return errno;
2347 1.1.1.2.10.1 bouyer
2348 1.1.1.2.10.1 bouyer /*
2349 1.1.1.2.10.1 bouyer * We read the header, payload, and trailer into the buffer we have, that
2350 1.1.1.2.10.1 bouyer * many bytes before the previous record we read.
2351 1.1.1.2.10.1 bouyer */
2352 1.1.1.2.10.1 bouyer errno = 0;
2353 1.1.1.2.10.1 bouyer bytes = krb5_storage_read(sp, entries->p - entry_len, entry_len);
2354 1.1.1.2.10.1 bouyer ret = errno;
2355 1.1.1.2.10.1 bouyer if (bytes < 0 || bytes != entry_len)
2356 1.1.1.2.10.1 bouyer return ret ? ret : EIO;
2357 1.1.1.2.10.1 bouyer
2358 1.1.1.2.10.1 bouyer entries->first = ver;
2359 1.1.1.2.10.1 bouyer --entries->nentries;
2360 1.1.1.2.10.1 bouyer entries->p -= entry_len;
2361 1.1.1.2.10.1 bouyer return (entries->p == base) ? -1 : 0;
2362 1.1.1.2.10.1 bouyer }
2363 1.1.1.2.10.1 bouyer
2364 1.1.1.2.10.1 bouyer
2365 1.1.1.2.10.1 bouyer /*
2366 1.1.1.2.10.1 bouyer * Serialize a tail fragment of the log as a krb5_data, this is constrained to
2367 1.1.1.2.10.1 bouyer * at most `maxbytes' bytes and to at most `maxentries' entries if not zero.
2368 1.1.1.2.10.1 bouyer */
2369 1.1.1.2.10.1 bouyer static kadm5_ret_t
2370 1.1.1.2.10.1 bouyer load_entries(kadm5_server_context *context, krb5_data *p,
2371 1.1.1.2.10.1 bouyer size_t maxentries, size_t maxbytes,
2372 1.1.1.2.10.1 bouyer uint32_t *first, uint32_t *last)
2373 1.1 elric {
2374 1.1.1.2.10.1 bouyer struct load_entries_data entries;
2375 1.1 elric kadm5_ret_t ret;
2376 1.1.1.2.10.1 bouyer unsigned char *base;
2377 1.1 elric
2378 1.1.1.2.10.1 bouyer krb5_data_zero(p);
2379 1.1 elric
2380 1.1.1.2.10.1 bouyer *first = 0;
2381 1.1 elric
2382 1.1.1.2.10.1 bouyer memset(&entries, 0, sizeof(entries));
2383 1.1.1.2.10.1 bouyer entries.entries = NULL;
2384 1.1.1.2.10.1 bouyer entries.p = NULL;
2385 1.1.1.2.10.1 bouyer entries.maxentries = maxentries;
2386 1.1.1.2.10.1 bouyer entries.maxbytes = maxbytes;
2387 1.1 elric
2388 1.1.1.2.10.1 bouyer /* Figure out how many bytes it will take */
2389 1.1.1.2.10.1 bouyer ret = kadm5_log_foreach(context, kadm_backward | kadm_confirmed,
2390 1.1.1.2.10.1 bouyer NULL, load_entries_cb, &entries);
2391 1.1 elric if (ret)
2392 1.1.1.2.10.1 bouyer return ret;
2393 1.1.1.2.10.1 bouyer
2394 1.1.1.2.10.1 bouyer /*
2395 1.1.1.2.10.1 bouyer * If no entries fit our limits, we do not truncate, instead the caller can
2396 1.1.1.2.10.1 bouyer * call kadm5_log_reinit() if desired.
2397 1.1.1.2.10.1 bouyer */
2398 1.1.1.2.10.1 bouyer if (entries.bytes == 0)
2399 1.1.1.2.10.1 bouyer return 0;
2400 1.1 elric
2401 1.1.1.2.10.1 bouyer ret = krb5_data_alloc(p, entries.bytes);
2402 1.1 elric if (ret)
2403 1.1.1.2.10.1 bouyer return ret;
2404 1.1 elric
2405 1.1.1.2.10.1 bouyer *first = entries.first;
2406 1.1.1.2.10.1 bouyer *last = entries.last;
2407 1.1.1.2.10.1 bouyer entries.entries = p;
2408 1.1.1.2.10.1 bouyer base = (unsigned char *)entries.entries->data;
2409 1.1.1.2.10.1 bouyer entries.p = base + entries.bytes;
2410 1.1.1.2.10.1 bouyer
2411 1.1.1.2.10.1 bouyer ret = kadm5_log_foreach(context, kadm_backward | kadm_confirmed,
2412 1.1.1.2.10.1 bouyer NULL, load_entries_cb, &entries);
2413 1.1.1.2.10.1 bouyer if (ret == 0 &&
2414 1.1.1.2.10.1 bouyer (entries.nentries || entries.p != base || entries.first != *first))
2415 1.1.1.2.10.1 bouyer ret = KADM5_LOG_CORRUPT;
2416 1.1 elric if (ret)
2417 1.1.1.2.10.1 bouyer krb5_data_free(p);
2418 1.1.1.2.10.1 bouyer return ret;
2419 1.1.1.2.10.1 bouyer }
2420 1.1.1.2.10.1 bouyer
2421 1.1.1.2.10.1 bouyer /*
2422 1.1.1.2.10.1 bouyer * Truncate the log, retaining at most `keep' entries and at most `maxbytes'.
2423 1.1.1.2.10.1 bouyer * If `maxbytes' is zero, keep at most the default log size limit.
2424 1.1.1.2.10.1 bouyer */
2425 1.1.1.2.10.1 bouyer kadm5_ret_t
2426 1.1.1.2.10.1 bouyer kadm5_log_truncate(kadm5_server_context *context, size_t keep, size_t maxbytes)
2427 1.1.1.2.10.1 bouyer {
2428 1.1.1.2.10.1 bouyer kadm5_ret_t ret;
2429 1.1.1.2.10.1 bouyer uint32_t first, last, last_tstamp;
2430 1.1.1.2.10.1 bouyer time_t now = time(NULL);
2431 1.1.1.2.10.1 bouyer krb5_data entries;
2432 1.1.1.2.10.1 bouyer krb5_storage *sp;
2433 1.1.1.2.10.1 bouyer ssize_t bytes;
2434 1.1.1.2.10.1 bouyer uint64_t sz;
2435 1.1.1.2.10.1 bouyer off_t off;
2436 1.1.1.2.10.1 bouyer
2437 1.1.1.2.10.1 bouyer if (maxbytes == 0)
2438 1.1.1.2.10.1 bouyer maxbytes = get_max_log_size(context->context);
2439 1.1.1.2.10.1 bouyer
2440 1.1.1.2.10.1 bouyer if (strcmp(context->log_context.log_file, "/dev/null") == 0)
2441 1.1.1.2.10.1 bouyer return 0;
2442 1.1.1.2.10.1 bouyer
2443 1.1.1.2.10.1 bouyer if (context->log_context.read_only)
2444 1.1.1.2.10.1 bouyer return EROFS;
2445 1.1.1.2.10.1 bouyer
2446 1.1.1.2.10.1 bouyer /* Get the desired records. */
2447 1.1.1.2.10.1 bouyer krb5_data_zero(&entries);
2448 1.1.1.2.10.1 bouyer ret = load_entries(context, &entries, keep, maxbytes, &first, &last);
2449 1.1.1.2.10.1 bouyer if (ret)
2450 1.1.1.2.10.1 bouyer return ret;
2451 1.1.1.2.10.1 bouyer
2452 1.1.1.2.10.1 bouyer if (first == 0) {
2453 1.1.1.2.10.1 bouyer /*
2454 1.1.1.2.10.1 bouyer * No records found/fit within resource limits. The caller should call
2455 1.1.1.2.10.1 bouyer * kadm5_log_reinit(context) to truly truncate and reset the log to
2456 1.1.1.2.10.1 bouyer * version 0, else call again with better limits.
2457 1.1.1.2.10.1 bouyer */
2458 1.1.1.2.10.1 bouyer krb5_data_free(&entries);
2459 1.1.1.2.10.1 bouyer return EINVAL;
2460 1.1.1.2.10.1 bouyer }
2461 1.1.1.2.10.1 bouyer
2462 1.1.1.2.10.1 bouyer /* Check that entries.length won't overflow off_t */
2463 1.1.1.2.10.1 bouyer sz = LOG_UBER_SZ + entries.length;
2464 1.1.1.2.10.1 bouyer off = (off_t)sz;
2465 1.1.1.2.10.1 bouyer if (off < 0 || off != sz || sz < entries.length) {
2466 1.1.1.2.10.1 bouyer krb5_data_free(&entries);
2467 1.1.1.2.10.1 bouyer return EOVERFLOW; /* caller should ask for fewer entries */
2468 1.1.1.2.10.1 bouyer }
2469 1.1.1.2.10.1 bouyer
2470 1.1.1.2.10.1 bouyer /* Truncate to zero size and seek to zero offset */
2471 1.1.1.2.10.1 bouyer if (ftruncate(context->log_context.log_fd, 0) < 0 ||
2472 1.1.1.2.10.1 bouyer lseek(context->log_context.log_fd, 0, SEEK_SET) < 0) {
2473 1.1.1.2.10.1 bouyer krb5_data_free(&entries);
2474 1.1.1.2.10.1 bouyer return errno;
2475 1.1.1.2.10.1 bouyer }
2476 1.1.1.2.10.1 bouyer
2477 1.1.1.2.10.1 bouyer /*
2478 1.1.1.2.10.1 bouyer * Write the uber record and then the records loaded. Confirm the entries
2479 1.1.1.2.10.1 bouyer * after writing them.
2480 1.1.1.2.10.1 bouyer *
2481 1.1.1.2.10.1 bouyer * If we crash then the log may not have all the entries we want, and
2482 1.1.1.2.10.1 bouyer * replaying only some of the entries will leave us in a bad state.
2483 1.1.1.2.10.1 bouyer * Additionally, we don't have mathematical proof that replaying the last
2484 1.1.1.2.10.1 bouyer * N>1 entries is always idempotent. And though we believe we can make
2485 1.1.1.2.10.1 bouyer * such replays idempotent, they would still leave the HDB with
2486 1.1.1.2.10.1 bouyer * intermediate states that would not have occurred on the master.
2487 1.1.1.2.10.1 bouyer *
2488 1.1.1.2.10.1 bouyer * By initially setting the offset in the uber record to 0, the log will be
2489 1.1.1.2.10.1 bouyer * seen as invalid should we crash here, thus the only
2490 1.1.1.2.10.1 bouyer * harm will be that we'll reinitialize the log and force full props.
2491 1.1.1.2.10.1 bouyer *
2492 1.1.1.2.10.1 bouyer * We can't use the normal kadm5_log_*() machinery for this because
2493 1.1.1.2.10.1 bouyer * we must set specific version numbers and timestamps. To keep
2494 1.1.1.2.10.1 bouyer * things simple we don't try to do a single atomic write here as we
2495 1.1.1.2.10.1 bouyer * do in kadm5_log_flush().
2496 1.1.1.2.10.1 bouyer *
2497 1.1.1.2.10.1 bouyer * We really do want to keep the new first entry's version and
2498 1.1.1.2.10.1 bouyer * timestamp so we don't trip up iprop.
2499 1.1.1.2.10.1 bouyer *
2500 1.1.1.2.10.1 bouyer * Keep this in sync with kadm5_log_nop().
2501 1.1.1.2.10.1 bouyer */
2502 1.1.1.2.10.1 bouyer sp = krb5_storage_from_fd(context->log_context.log_fd);
2503 1.1.1.2.10.1 bouyer if (sp == NULL) {
2504 1.1.1.2.10.1 bouyer ret = errno;
2505 1.1.1.2.10.1 bouyer krb5_warn(context->context, ret, "Unable to keep entries");
2506 1.1.1.2.10.1 bouyer krb5_data_free(&entries);
2507 1.1.1.2.10.1 bouyer return errno;
2508 1.1.1.2.10.1 bouyer }
2509 1.1.1.2.10.1 bouyer ret = krb5_store_uint32(sp, 0);
2510 1.1.1.2.10.1 bouyer if (ret == 0)
2511 1.1.1.2.10.1 bouyer ret = krb5_store_uint32(sp, now);
2512 1.1.1.2.10.1 bouyer if (ret == 0)
2513 1.1.1.2.10.1 bouyer ret = krb5_store_uint32(sp, kadm_nop); /* end of preamble */
2514 1.1.1.2.10.1 bouyer if (ret == 0)
2515 1.1.1.2.10.1 bouyer ret = krb5_store_uint32(sp, LOG_UBER_LEN); /* end of header */
2516 1.1.1.2.10.1 bouyer if (ret == 0)
2517 1.1.1.2.10.1 bouyer ret = krb5_store_uint64(sp, LOG_UBER_SZ);
2518 1.1.1.2.10.1 bouyer if (ret == 0)
2519 1.1.1.2.10.1 bouyer ret = krb5_store_uint32(sp, now);
2520 1.1.1.2.10.1 bouyer if (ret == 0)
2521 1.1.1.2.10.1 bouyer ret = krb5_store_uint32(sp, last);
2522 1.1.1.2.10.1 bouyer if (ret == 0)
2523 1.1.1.2.10.1 bouyer ret = krb5_store_uint32(sp, LOG_UBER_LEN);
2524 1.1.1.2.10.1 bouyer if (ret == 0)
2525 1.1.1.2.10.1 bouyer ret = krb5_store_uint32(sp, 0); /* end of trailer */
2526 1.1.1.2.10.1 bouyer if (ret == 0) {
2527 1.1.1.2.10.1 bouyer bytes = krb5_storage_write(sp, entries.data, entries.length);
2528 1.1.1.2.10.1 bouyer if (bytes == -1)
2529 1.1.1.2.10.1 bouyer ret = errno;
2530 1.1.1.2.10.1 bouyer }
2531 1.1.1.2.10.1 bouyer if (ret == 0)
2532 1.1.1.2.10.1 bouyer ret = krb5_storage_fsync(sp);
2533 1.1.1.2.10.1 bouyer /* Confirm all the records now */
2534 1.1.1.2.10.1 bouyer if (ret == 0) {
2535 1.1.1.2.10.1 bouyer if (krb5_storage_seek(sp, LOG_HEADER_SZ, SEEK_SET) == -1)
2536 1.1.1.2.10.1 bouyer ret = errno;
2537 1.1.1.2.10.1 bouyer }
2538 1.1.1.2.10.1 bouyer if (ret == 0)
2539 1.1.1.2.10.1 bouyer ret = krb5_store_uint64(sp, off);
2540 1.1.1.2.10.1 bouyer krb5_data_free(&entries);
2541 1.1.1.2.10.1 bouyer krb5_storage_free(sp);
2542 1.1 elric
2543 1.1.1.2.10.1 bouyer if (ret) {
2544 1.1.1.2.10.1 bouyer krb5_warn(context->context, ret, "Unable to keep entries");
2545 1.1.1.2.10.1 bouyer (void) ftruncate(context->log_context.log_fd, LOG_UBER_SZ);
2546 1.1.1.2.10.1 bouyer (void) lseek(context->log_context.log_fd, 0, SEEK_SET);
2547 1.1.1.2.10.1 bouyer return ret;
2548 1.1.1.2.10.1 bouyer }
2549 1.1.1.2.10.1 bouyer
2550 1.1.1.2.10.1 bouyer /* Done. Now rebuild the log_context state. */
2551 1.1.1.2.10.1 bouyer (void) lseek(context->log_context.log_fd, off, SEEK_SET);
2552 1.1.1.2.10.1 bouyer sp = kadm5_log_goto_end(context, context->log_context.log_fd);
2553 1.1.1.2.10.1 bouyer if (sp == NULL)
2554 1.1.1.2.10.1 bouyer return ENOMEM;
2555 1.1.1.2.10.1 bouyer ret = get_version_prev(sp, &context->log_context.version, &last_tstamp);
2556 1.1.1.2.10.1 bouyer context->log_context.last_time = last_tstamp;
2557 1.1.1.2.10.1 bouyer krb5_storage_free(sp);
2558 1.1.1.2.10.1 bouyer return ret;
2559 1.1.1.2.10.1 bouyer }
2560 1.1.1.2.10.1 bouyer
2561 1.1.1.2.10.1 bouyer /*
2562 1.1.1.2.10.1 bouyer * "Truncate" the log if not read only and over the desired maximum size. We
2563 1.1.1.2.10.1 bouyer * attempt to retain 1/4 of the existing storage.
2564 1.1.1.2.10.1 bouyer *
2565 1.1.1.2.10.1 bouyer * Called after successful log recovery, so at this point we must have no
2566 1.1.1.2.10.1 bouyer * unconfirmed entries in the log.
2567 1.1.1.2.10.1 bouyer */
2568 1.1.1.2.10.1 bouyer static kadm5_ret_t
2569 1.1.1.2.10.1 bouyer truncate_if_needed(kadm5_server_context *context)
2570 1.1.1.2.10.1 bouyer {
2571 1.1.1.2.10.1 bouyer kadm5_ret_t ret = 0;
2572 1.1.1.2.10.1 bouyer kadm5_log_context *log_context = &context->log_context;
2573 1.1.1.2.10.1 bouyer size_t maxbytes;
2574 1.1.1.2.10.1 bouyer struct stat st;
2575 1.1.1.2.10.1 bouyer
2576 1.1.1.2.10.1 bouyer if (log_context->log_fd == -1 || log_context->read_only)
2577 1.1.1.2.10.1 bouyer return 0;
2578 1.1.1.2.10.1 bouyer if (strcmp(context->log_context.log_file, "/dev/null") == 0)
2579 1.1.1.2.10.1 bouyer return 0;
2580 1.1.1.2.10.1 bouyer
2581 1.1.1.2.10.1 bouyer maxbytes = get_max_log_size(context->context);
2582 1.1.1.2.10.1 bouyer if (maxbytes <= 0)
2583 1.1.1.2.10.1 bouyer return 0;
2584 1.1.1.2.10.1 bouyer
2585 1.1.1.2.10.1 bouyer if (fstat(log_context->log_fd, &st) == -1)
2586 1.1.1.2.10.1 bouyer return errno;
2587 1.1.1.2.10.1 bouyer if (st.st_size == (size_t)st.st_size && (size_t)st.st_size <= maxbytes)
2588 1.1.1.2.10.1 bouyer return 0;
2589 1.1.1.2.10.1 bouyer
2590 1.1.1.2.10.1 bouyer /* Shrink the log by a factor of 4 */
2591 1.1.1.2.10.1 bouyer ret = kadm5_log_truncate(context, 0, maxbytes/4);
2592 1.1.1.2.10.1 bouyer return ret == EINVAL ? 0 : ret;
2593 1.1 elric }
2594 1.1 elric
2595 1.1 elric #ifndef NO_UNIX_SOCKETS
2596 1.1 elric
2597 1.1 elric static char *default_signal = NULL;
2598 1.1 elric static HEIMDAL_MUTEX signal_mutex = HEIMDAL_MUTEX_INITIALIZER;
2599 1.1 elric
2600 1.1 elric const char *
2601 1.1 elric kadm5_log_signal_socket(krb5_context context)
2602 1.1 elric {
2603 1.1.1.2.10.1 bouyer int ret = 0;
2604 1.1.1.2.10.1 bouyer
2605 1.1 elric HEIMDAL_MUTEX_lock(&signal_mutex);
2606 1.1 elric if (!default_signal)
2607 1.1.1.2.10.1 bouyer ret = asprintf(&default_signal, "%s/signal", hdb_db_dir(context));
2608 1.1.1.2.10.1 bouyer if (ret == -1)
2609 1.1.1.2.10.1 bouyer default_signal = NULL;
2610 1.1 elric HEIMDAL_MUTEX_unlock(&signal_mutex);
2611 1.1 elric
2612 1.1 elric return krb5_config_get_string_default(context,
2613 1.1 elric NULL,
2614 1.1 elric default_signal,
2615 1.1 elric "kdc",
2616 1.1 elric "signal_socket",
2617 1.1 elric NULL);
2618 1.1 elric }
2619 1.1 elric
2620 1.1 elric #else /* NO_UNIX_SOCKETS */
2621 1.1 elric
2622 1.1 elric #define SIGNAL_SOCKET_HOST "127.0.0.1"
2623 1.1 elric #define SIGNAL_SOCKET_PORT "12701"
2624 1.1 elric
2625 1.1 elric kadm5_ret_t
2626 1.1 elric kadm5_log_signal_socket_info(krb5_context context,
2627 1.1 elric int server_end,
2628 1.1 elric struct addrinfo **ret_addrs)
2629 1.1 elric {
2630 1.1 elric struct addrinfo hints;
2631 1.1 elric struct addrinfo *addrs = NULL;
2632 1.1 elric kadm5_ret_t ret = KADM5_FAILURE;
2633 1.1 elric int wsret;
2634 1.1 elric
2635 1.1 elric memset(&hints, 0, sizeof(hints));
2636 1.1 elric
2637 1.1 elric hints.ai_flags = AI_NUMERICHOST;
2638 1.1 elric if (server_end)
2639 1.1 elric hints.ai_flags |= AI_PASSIVE;
2640 1.1 elric hints.ai_family = AF_INET;
2641 1.1 elric hints.ai_socktype = SOCK_STREAM;
2642 1.1 elric hints.ai_protocol = IPPROTO_TCP;
2643 1.1 elric
2644 1.1 elric wsret = getaddrinfo(SIGNAL_SOCKET_HOST,
2645 1.1 elric SIGNAL_SOCKET_PORT,
2646 1.1 elric &hints, &addrs);
2647 1.1 elric
2648 1.1 elric if (wsret != 0) {
2649 1.1 elric krb5_set_error_message(context, KADM5_FAILURE,
2650 1.1 elric "%s", gai_strerror(wsret));
2651 1.1 elric goto done;
2652 1.1 elric }
2653 1.1 elric
2654 1.1 elric if (addrs == NULL) {
2655 1.1 elric krb5_set_error_message(context, KADM5_FAILURE,
2656 1.1 elric "getaddrinfo() failed to return address list");
2657 1.1 elric goto done;
2658 1.1 elric }
2659 1.1 elric
2660 1.1 elric *ret_addrs = addrs;
2661 1.1 elric addrs = NULL;
2662 1.1 elric ret = 0;
2663 1.1 elric
2664 1.1 elric done:
2665 1.1 elric if (addrs)
2666 1.1 elric freeaddrinfo(addrs);
2667 1.1 elric return ret;
2668 1.1 elric }
2669 1.1 elric
2670 1.1 elric #endif
2671