chiark / gitweb /
fbb7f11e0dc6e9458d2178f4e85e30225f0927ec
[catacomb] / pixie-common.c
1 /* -*-c-*-
2  *
3  * $Id$
4  *
5  * Common code for Pixie client and server (Unix-specific)
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 /*----- Header files ------------------------------------------------------*/
31
32 #include <errno.h>
33 #include <stddef.h>
34 #include <stdio.h>
35 #include <stdlib.h>
36 #include <string.h>
37
38 #include <sys/types.h>
39 #include <unistd.h>
40 #include <fcntl.h>
41 #include <termios.h>
42 #include <pwd.h>
43
44 #include <sys/socket.h>
45 #include <sys/un.h>
46
47 #include <mLib/alloc.h>
48 #include <mLib/dstr.h>
49 #include <mLib/str.h>
50
51 #include "pixie.h"
52
53 /*----- Main code ---------------------------------------------------------*/
54
55 /* --- @pixie_address@ --- *
56  *
57  * Arguments:   @const char *sock@ = pointer to socket name
58  *              @size_t *psz@ = where to write the address size
59  *
60  * Returns:     Pointer to filled-in Unix-domain socket address.
61  *
62  * Use:         Returns a Unix-domain socket address to use to find the
63  *              passphrase pixie.
64  */
65
66 struct sockaddr_un *pixie_address(const char *sock, size_t *psz)
67 {
68   dstr d = DSTR_INIT;
69
70   /* --- Get the default socket path if none specified --- */
71
72   if (!sock)
73     sock = getenv("CATACOMB_PIXIE_SOCKET");
74   if (!sock)
75     sock = "%h/.catacomb/pixie";
76
77   /* --- Substitute interesting sequences in the path --- */
78
79   {
80     const char *q, *qq;
81
82     q = sock;
83     for (;;) {
84       qq = strchr(q, '%');
85       if (!qq || !qq[1]) {
86         DPUTS(&d, q);
87         break;
88       }
89       DPUTM(&d, q, qq - q);
90       q = qq + 1;
91       switch (*q) {
92         case 'u':
93           qq = getenv("USER");
94           if (!qq)
95             qq = getenv("LOGNAME");
96           if (!qq) {
97             struct passwd *pw = getpwuid(getuid());
98             if (pw)
99               qq = pw->pw_name;
100             else
101               qq = "<unknown>";
102           }
103           DPUTS(&d, qq);
104           break;
105         case 'h':
106           qq = getenv("HOME");
107           if (!qq) {
108             struct passwd *pw = getpwuid(getuid());
109             if (pw)
110               qq = pw->pw_dir;
111             else
112               qq = "<unknown>";
113           }
114           DPUTS(&d, qq);
115           break;
116         default:
117           DPUTC(&d, '%');
118           DPUTC(&d, *q);
119           break;
120       }
121       q++;
122     }
123     DPUTZ(&d);
124   }
125
126   /* --- Allocate and initialize the socket address --- */
127
128   {
129     struct sockaddr_un *sun;
130     size_t bsz = offsetof(struct sockaddr_un, sun_path);
131     *psz = bsz + d.len + 1;
132     sun = xmalloc(bsz + d.len + 1);
133     memset(sun, 0, bsz);
134     sun->sun_family = AF_UNIX;
135     memcpy(sun->sun_path, d.buf, d.len + 1);
136     dstr_destroy(&d);
137     return (sun);
138   }
139 }
140
141 /* --- @pixie_fdline@ --- *
142  *
143  * Arguments:   @int fd@ = file descriptor to read from
144  *              @char *buf@ = pointer to buffer
145  *              @size_t sz@ = size of buffer
146  *
147  * Returns:     ---
148  *
149  * Use:         Reads a line from a file descriptor.  The read is done one
150  *              character at a time.  If the entire line won't fit, the end
151  *              is truncated.  The line is null terminated.
152  */
153
154 void pixie_fdline(int fd, char *buf, size_t sz)
155 {
156   char *p = buf;
157   char *q = p + sz - 1;
158
159   for (;;) {
160     char c;
161     if (read(fd, &c, 1) < 1)
162       break;
163     if (c == '\n')
164       break;
165     if (p < q)
166       *p++ = c;
167   }
168   *p = 0;
169 }
170
171 /* --- @pixie_getpass@ --- *
172  *
173  * Arguments:   @const char *prompt@ = pointer to prompt string
174  *              @char *buf@ = pointer to buffer
175  *              @size_t sz@ = size of buffer
176  *
177  * Returns:     Zero if it worked OK, nonzero otherwise.
178  *
179  * Use:         Reads a passphrase from the terminal or some other requested
180  *              source.
181  */
182
183 int pixie_getpass(const char *prompt, char *buf, size_t sz)
184 {
185   const char *pfd = getenv("CATACOMB_PASSPHRASE_FD");
186   int fd = 0;
187
188   /* --- See whether a terminal is what's wanted --- */
189
190   if (pfd) {
191     fd = atoi(pfd);
192     pixie_fdline(fd, buf, sz);
193   } else {
194     struct termios ta;
195     struct termios ota;
196     char nl = '\n';
197
198     if ((fd = open("/dev/tty", O_RDWR)) < 0)
199       goto fail_0;
200     if (tcgetattr(fd, &ta) < 0)
201       goto fail_1;
202     ota = ta;
203     ta.c_lflag &= ~(ECHO | ISIG);
204     if (tcsetattr(fd, TCSAFLUSH, &ta))
205       goto fail_1;
206     write(fd, prompt, strlen(prompt));
207     pixie_fdline(fd, buf, sz);
208     tcsetattr(fd, TCSAFLUSH, &ota);
209     write(fd, &nl, 1);
210     close(fd);
211   }
212   return (0);
213
214   /* --- Tidy up if things went wrong --- */
215
216 fail_1:
217   close(fd);
218 fail_0:
219   return (-1);
220 }
221
222 /* --- @pixie_open@ --- *
223  *
224  * Arguments:   @const char *sock@ = path to pixie socket
225  *
226  * Returns:     Less than zero if it failed, or file descriptor.
227  *
228  * Use:         Opens a connection to a passphrase pixie.
229  */
230
231 int pixie_open(const char *sock)
232 {
233   struct sockaddr_un *sun;
234   size_t sz;
235   int fd;
236
237   /* --- Open the connection --- */
238
239   if ((fd = socket(PF_UNIX, SOCK_STREAM, 0)) < 0)
240     goto fail_0;
241   sun = pixie_address(sock, &sz);
242   if (connect(fd, (struct sockaddr *)sun, sz))
243     goto fail_1;
244   xfree(sun);
245   return (fd);
246
247   /* --- Tidy up if things went wrong --- */
248
249 fail_1:
250   xfree(sun);
251   close(fd);
252 fail_0:
253   return (-1);
254 }
255
256 /* --- @pixie_read@ --- *
257  *
258  * Arguments:   @int fd@ = connection to passphrase pixie
259  *              @const char *tag@ = pointer to tag string
260  *              @unsigned mode@ = reading mode
261  *              @char *buf@ = pointer to destination buffer
262  *              @size_t sz@ = size of the buffer
263  *
264  * Returns:     Zero if all went well, @-1@ if the read fails, @+1@ to
265  *              request the passphrase from the user.
266  *
267  * Use:         Reads a passphrase from the pixie.
268  */
269
270 int pixie_read(int fd, const char *tag, unsigned mode, char *buf, size_t sz)
271 {
272   dstr d = DSTR_INIT;
273   char *p, *q;
274
275   /* --- Send the request --- */
276
277   dstr_putf(&d, "%s %s\n", mode == PMODE_READ ? "PASS" : "VERIFY", tag);
278   write(fd, d.buf, d.len);
279   dstr_destroy(&d);
280
281   /* --- Sort out the result --- */
282
283 again:
284   pixie_fdline(fd, buf, sz);
285   p = buf;
286   if ((q = str_getword(&p)) == 0)
287     return (-1);
288   if (strcmp(q, "INFO") == 0)
289     goto again;
290   else if (strcmp(q, "MISSING") == 0)
291     return (+1);
292   else if (strcmp(q, "OK") != 0)
293     return (-1);
294
295   /* --- Return the final answer --- */
296
297   if (p)
298     memmove(buf, p, strlen(p) + 1);
299   else
300     *buf = 0;
301   return (0);
302 }
303
304 /* --- @pixie_set@ --- *
305  *
306  * Arguments:   @int fd@ = pixie file descriptor
307  *              @const char *tag@ = pointer to tag string
308  *              @const char *phrase@ = pointer to passphrase string
309  *
310  * Returns:     ---
311  *
312  * Use:         Sends a passphrase to the passphrase pixie.
313  */
314
315 void pixie_set(int fd, const char *tag, const char *phrase)
316 {
317   dstr d = DSTR_INIT;
318   char buf[16];
319   size_t sz = strlen(phrase);
320   char nl = '\n';
321   char *p, *q;
322
323   /* --- Send the request --- *
324    *
325    * I didn't want to copy it out of the caller's buffer.  @writev@ may
326    * produce a copy, too, so I didn't do that either.
327    */
328
329   dstr_putf(&d, "SET %s -- ", tag);
330   write(fd, d.buf, d.len);
331   write(fd, phrase, sz);
332   write(fd, &nl, 1);
333   dstr_destroy(&d);
334
335   /* --- Pick up the pieces --- */
336
337 again:
338   pixie_fdline(fd, buf, sizeof(buf));
339   p = buf;
340   if ((q = str_getword(&p)) != 0 && strcmp(q, "INFO") == 0)
341     goto again;
342 }
343
344 /* --- @pixie_cancel@ --- *
345  *
346  * Arguments:   @int fd@ = pixie file descriptor
347  *              @const char *tag@ = pointer to tag string
348  *
349  * Returns:     ---
350  *
351  * Use:         Cancels a passphrase if it turns out to be bogus.
352  */
353
354 void pixie_cancel(int fd, const char *tag)
355 {
356   dstr d = DSTR_INIT;
357   char buf[16];
358   char *p, *q;
359
360   /* --- Send the request --- */
361
362   dstr_putf(&d, "FLUSH %s\n", tag);
363   write(fd, d.buf, d.len);
364   dstr_destroy(&d);
365
366   /* --- Sort out the result --- */
367
368 again:
369   pixie_fdline(fd, buf, sizeof(buf));
370   p = buf;
371   if ((q = str_getword(&p)) != 0 && strcmp(q, "INFO") == 0)
372     goto again;
373 }
374
375 /*----- That's all, folks -------------------------------------------------*/
376