chiark / gitweb /
Major key-management revision.
[catacomb] / key-file.c
1 /* -*-c-*-
2  *
3  * $Id: key-file.c,v 1.1 1999/12/22 15:47:48 mdw Exp $
4  *
5  * System-dependent key filing operations
6  *
7  * (c) 1999 Straylight/Edgeware
8  */
9
10 /*----- Licensing notice --------------------------------------------------* 
11  *
12  * This file is part of Catacomb.
13  *
14  * Catacomb is free software; you can redistribute it and/or modify
15  * it under the terms of the GNU Library General Public License as
16  * published by the Free Software Foundation; either version 2 of the
17  * License, or (at your option) any later version.
18  * 
19  * Catacomb is distributed in the hope that it will be useful,
20  * but WITHOUT ANY WARRANTY; without even the implied warranty of
21  * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
22  * GNU Library General Public License for more details.
23  * 
24  * You should have received a copy of the GNU Library General Public
25  * License along with Catacomb; if not, write to the Free
26  * Software Foundation, Inc., 59 Temple Place - Suite 330, Boston,
27  * MA 02111-1307, USA.
28  */
29
30 /*----- Revision history --------------------------------------------------* 
31  *
32  * $Log: key-file.c,v $
33  * Revision 1.1  1999/12/22 15:47:48  mdw
34  * Major key-management revision.
35  *
36  */
37
38 /*----- Header files ------------------------------------------------------*/
39
40 #include <errno.h>
41 #include <stdio.h>
42 #include <stdlib.h>
43 #include <string.h>
44
45 #include <sys/types.h>
46 #include <sys/time.h>
47 #include <sys/stat.h>
48 #include <unistd.h>
49 #include <fcntl.h>
50
51 #include <mLib/dstr.h>
52 #include <mLib/lock.h>
53
54 #include "key.h"
55
56 /*----- Main code ---------------------------------------------------------*/
57
58 /* --- @fdcopy@ --- *
59  *
60  * Arguments:   @int source@ = source file descriptor
61  *              @int dest@ = destination file descriptor
62  *
63  * Returns:     Zero if OK, nonzero otherwise.
64  *
65  * Use:         Copies data from one file descriptor to another.
66  */
67
68 static int fdcopy(int source, int dest)
69 {
70   char buf[4096];
71
72   if (lseek(source, 0, SEEK_SET) < 0||
73       lseek(dest, 0, SEEK_SET) < 0 ||
74       ftruncate(dest, 0) < 0)
75     return (-1);
76   for (;;) {
77     int n = read(source, buf, sizeof(buf));
78     if (n < 0)
79       return (-1);
80     else if (n == 0)
81       break;
82     else if (write(dest, buf, n) < 0)
83       return (-1);
84   }
85   return (0);
86 }
87
88 /* --- @key_save@ --- *
89  *
90  * Arguments:   @key_file *f@ = pointer to key file block
91  *
92  * Returns:     A @KWRITE_@ code indicating how well it worked.
93  *
94  * Use:         Writes a key file's data back to the actual file.  This code
95  *              is extremely careful about error handling.  It should usually
96  *              be able to back out somewhere sensible, but it can tell when
97  *              it's got itself into a real pickle and starts leaving well
98  *              alone.
99  *
100  *              Callers, please make sure that you ring alarm bells when this
101  *              function returns @KWRITE_BROKEN@.
102  */
103
104 int key_save(key_file *f)
105 {
106   dstr n_older = DSTR_INIT, n_old = DSTR_INIT, n_new = DSTR_INIT;
107   int rc = KWRITE_FAIL;
108
109   if (!(f->f & KF_MODIFIED))
110     return (KWRITE_OK);
111
112   /* --- Write a new key file out --- *
113    *
114    * Check for an error after each key line.  This ought to be enough.
115    * Checking after each individual byte write and @fprintf@ isn't much fun.
116    */
117
118   dstr_putf(&n_new, "%s.new", f->name);
119
120   {
121     key *k;
122     key_iter i;
123     FILE *fp;
124
125     if ((fp = fopen(n_new.buf, "w")) == 0)
126       goto fail_open;
127
128     for (key_mkiter(&i, f); (k = key_next(&i)) != 0; ) {
129       if (key_extract(f, k, fp, 0)) {
130         fclose(fp);
131         goto fail_write;
132       }
133     }
134
135     if (fclose(fp))
136       goto fail_write;
137   }
138
139   /* --- Set up the other filenames --- */
140
141   dstr_putf(&n_older, "%s.older", f->name);
142   dstr_putf(&n_old, "%s.old", f->name);
143
144   /* --- Move the current backup on one --- *
145    *
146    * If the `older' file exists, then we're in need of attention.
147    */
148
149   {
150     struct stat st;
151     if (stat(n_older.buf, &st) == 0 || errno != ENOENT) {
152       errno = EEXIST;
153       rc = KWRITE_BROKEN;
154       goto fail_shift;
155     }
156     if (rename(n_old.buf, n_older.buf) && errno != ENOENT)
157       goto fail_shift;
158   }
159
160   /* --- Copy the current file to the backup --- */
161
162   {
163     int fd;
164     if ((fd = open(n_old.buf, O_WRONLY | O_CREAT | O_EXCL, 0600)) < 0)
165       goto fail_backup;
166     if (fdcopy(fileno(f->fp), fd)) {
167       close(fd);
168       goto fail_backup;
169     }
170     if (close(fd))
171       goto fail_backup;
172   }
173
174   /* --- Copy the newly created file to the current one --- *
175    *
176    * This is the dangerous bit.
177    */
178
179   {
180     int fd;
181     if ((fd = open(n_new.buf, O_RDONLY)) < 0)
182       goto fail_update;
183     if (fdcopy(fd, fileno(f->fp))) {
184       close(fd);
185       goto fail_update;
186     }
187     close(fd);
188     if (fsync(fileno(f->fp)))
189       goto fail_update;
190   }
191
192   /* --- Clean up --- *
193    *
194    * Remove the `new' file and the `older' backup.  Then we're done.
195    */
196
197   unlink(n_new.buf);
198   unlink(n_older.buf);
199   return (KWRITE_OK);
200
201   /* --- Failure while writing the new key file --- *
202    *
203    * I need to copy the backup back.  If that fails then I'm really stuffed.
204    * If not, then I might as well try to get the backups sorted back out
205    * again.
206    */
207
208 fail_update:
209   {
210     int fd;
211     int e = errno;
212
213     if ((fd = open(n_old.buf, O_RDONLY)) < 0)
214       rc = KWRITE_BROKEN;
215     else if (fdcopy(fd, fileno(f->fp))) {
216       close(fd);
217       rc = KWRITE_BROKEN;
218     } else {
219       close(fd);
220       if (fsync(fileno(f->fp)))
221         rc = KWRITE_BROKEN;
222     }
223
224     errno = e;
225     if (rc == KWRITE_BROKEN)
226       goto fail_shift;
227   }
228   /* Now drop through */
229
230   /* --- Failure while writing the new backup --- *
231    *
232    * The new backup isn't any use.  Try to recover the old one.
233    */
234
235 fail_backup:
236   {
237     int e = errno;
238     unlink(n_old.buf);
239     if (rename(n_older.buf, n_old.buf) && errno != ENOENT)
240       rc = KWRITE_BROKEN;
241     errno = e;
242   }
243   /* Now drop through */
244
245   /* --- Failure while demoting the current backup --- *
246    *
247    * Leave the completed output file there for the operator in case he wants
248    * to clean up.
249    */
250
251 fail_shift:
252   dstr_destroy(&n_new);
253   dstr_destroy(&n_old);
254   dstr_destroy(&n_older);
255   return (rc);
256   
257 /* --- Failure during write of new data --- *
258  *
259  * Clean up the new file and return.  These errors can never cause
260  * breakage.
261  */
262
263 fail_write:
264   unlink(n_new.buf);
265   fail_open:
266   dstr_destroy(&n_new);
267   return (rc);
268 }
269
270 /* --- @key_lockfile@ --- *
271  *
272  * Arguments:   @key_file *f@ = pointer to file structure to initialize
273  *              @const char *file@ = pointer to the file name
274  *              @int how@ = opening options (@KOPEN_*@).
275  *
276  * Returns:     Zero if it worked, nonzero otherwise.
277  *
278  * Use:         Opens a keyfile and stores the information needed for
279  *              continued access in the structure.
280  *
281  *              If the file is opened with @KOPEN_WRITE@, it's created if
282  *              necessary with read and write permissions for owner only, and
283  *              locked for update while it's open.
284  *
285  *              This is a system-dependent routine, and only really intended
286  *              for the private use of @key_open@.
287  */
288
289 int key_lockfile(key_file *f, const char *file, int how)
290 {
291   int of, lf;
292   const char *ff;
293   int fd;
294
295   /* --- Lots of things depend on whether we're writing --- */
296
297   switch (how) {
298     case KOPEN_READ:
299       of = O_RDONLY;
300       lf = LOCK_NONEXCL;
301       ff = "r";
302       break;
303     case KOPEN_WRITE:
304       of = O_RDWR | O_CREAT;
305       lf = LOCK_EXCL;
306       ff = "r+";
307       break;
308     default:
309       errno = EINVAL;
310       return (-1);
311   }
312
313   /* --- Open and lock the file --- */
314
315   if ((fd = open(file, of, 0600)) < 0)
316     return (-1);
317   if (fcntl(fd, F_SETFD, FD_CLOEXEC) < 0 ||
318       lock_file(fd, lf) < 0 ||
319       (f->fp = fdopen(fd, ff)) == 0) {
320     close(fd);
321     return (-1);
322   }
323
324   return (0);
325 }
326
327 /*----- That's all, folks -------------------------------------------------*/