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