assuan/
[gpgme.git] / assuan / assuan-socket.c
1 /* assuan-socket.c
2  * Copyright (C) 2004, 2005 Free Software Foundation, Inc.
3  *
4  * This file is part of Assuan.
5  *
6  * Assuan is free software; you can redistribute it and/or modify it
7  * under the terms of the GNU Lesser General Public License as
8  * published by the Free Software Foundation; either version 2.1 of
9  * the License, or (at your option) any later version.
10  *
11  * Assuan is distributed in the hope that it will be useful, but
12  * WITHOUT ANY WARRANTY; without even the implied warranty of
13  * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the GNU
14  * Lesser General Public License for more details.
15  *
16  * You should have received a copy of the GNU Lesser General Public
17  * License along with this program; if not, see <http://www.gnu.org/licenses/>.
18  */
19
20 #include <config.h>
21 #include <stdio.h>
22 #include <stdlib.h>
23 #ifdef HAVE_W32_SYSTEM
24 #define WIN32_LEAN_AND_MEAN
25 #include <windows.h>
26 #include <wincrypt.h>
27 #include <io.h>
28 #else
29 #include <sys/types.h>
30 #include <sys/socket.h>
31 #endif
32 #include <errno.h>
33 #include <sys/stat.h>
34 #include <fcntl.h>
35 #include <assert.h>
36
37 #include "assuan-defs.h"
38
39 /* Hacks for Slowaris.  */
40 #ifndef PF_LOCAL
41 # ifdef PF_UNIX
42 #  define PF_LOCAL PF_UNIX
43 # else
44 #  define PF_LOCAL AF_UNIX
45 # endif
46 #endif
47 #ifndef AF_LOCAL
48 # define AF_LOCAL AF_UNIX
49 #endif
50
51 #ifdef HAVE_W32_SYSTEM
52 #ifndef S_IRGRP
53 # define S_IRGRP 0
54 # define S_IWGRP 0
55 #endif
56 #endif
57
58
59 #ifdef HAVE_W32_SYSTEM
60 int
61 _assuan_sock_wsa2errno (int err)
62 {
63   switch (err)
64     {
65     case WSAENOTSOCK:
66       return EINVAL;
67     case WSAEWOULDBLOCK:
68       return EAGAIN;
69     case ERROR_BROKEN_PIPE:
70       return EPIPE;
71     case WSANOTINITIALISED:
72       return ENOSYS;
73     default:
74       return EIO;
75     }
76 }
77
78
79 /* W32: Fill BUFFER with LENGTH bytes of random.  Returns -1 on
80    failure, 0 on success.  Sets errno on failure.  */
81 static int
82 get_nonce (char *buffer, size_t nbytes) 
83 {
84   HCRYPTPROV prov;
85   int ret = -1;
86
87   if (!CryptAcquireContext (&prov, NULL, NULL, PROV_RSA_FULL, 
88                             (CRYPT_VERIFYCONTEXT|CRYPT_SILENT)) )
89     errno = ENODEV;
90   else 
91     {
92       if (!CryptGenRandom (prov, nbytes, buffer))
93         errno = ENODEV;
94       else
95         ret = 0;
96       CryptReleaseContext (prov, 0);
97     }
98   return ret;
99 }
100
101
102 /* W32: The buffer for NONCE needs to be at least 16 bytes.  Returns 0 on
103    success and sets errno on failure. */
104 static int
105 read_port_and_nonce (const char *fname, unsigned short *port, char *nonce)
106 {
107   FILE *fp;
108   char buffer[50], *p;
109   size_t nread;
110   int aval;
111
112   fp = fopen (fname, "rb");
113   if (!fp)
114     return -1;
115   nread = fread (buffer, 1, sizeof buffer - 1, fp);
116   fclose (fp);
117   if (!nread)
118     {
119       errno = ENOFILE;
120       return -1;
121     }
122   buffer[nread] = 0;
123   aval = atoi (buffer);
124   if (aval < 1 || aval > 65535)
125     {
126       errno = EINVAL;
127       return -1;
128     }
129   *port = (unsigned int)aval;
130   for (p=buffer; nread && *p != '\n'; p++, nread--)
131     ;
132   if (*p != '\n' || nread != 17)
133     {
134       errno = EINVAL;
135       return -1;
136     }
137   p++; nread--;
138   memcpy (nonce, p, 16);
139   return 0;
140 }
141 #endif /*HAVE_W32_SYSTEM*/
142
143
144
145 int
146 _assuan_close (assuan_fd_t fd)
147 {
148 #if defined (HAVE_W32_SYSTEM) && !defined(_ASSUAN_IN_GPGME_BUILD_ASSUAN)
149   int rc = closesocket (HANDLE2SOCKET(fd));
150   if (rc)
151     errno = _assuan_sock_wsa2errno (WSAGetLastError ());
152   if (rc && WSAGetLastError () == WSAENOTSOCK)
153     {
154       rc = CloseHandle (fd);
155       if (rc)
156         /* FIXME. */
157         errno = EIO;
158     }
159   return rc;
160 #else
161   return close (fd);
162 #endif
163 }
164
165
166 /* Return a new socket.  Note that under W32 we consider a socket the
167    same as an System Handle; all functions using such a handle know
168    about this dual use and act accordingly. */ 
169 assuan_fd_t
170 _assuan_sock_new (int domain, int type, int proto)
171 {
172 #ifdef HAVE_W32_SYSTEM
173   assuan_fd_t res;
174   if (domain == AF_UNIX || domain == AF_LOCAL)
175     domain = AF_INET;
176   res = SOCKET2HANDLE(socket (domain, type, proto));
177   if (res == ASSUAN_INVALID_FD)
178     errno = _assuan_sock_wsa2errno (WSAGetLastError ());
179   return res;
180 #else
181   return socket (domain, type, proto);
182 #endif
183 }
184
185
186 int
187 _assuan_sock_connect (assuan_fd_t sockfd, struct sockaddr *addr, int addrlen)
188 {
189 #ifdef HAVE_W32_SYSTEM
190   if (addr->sa_family == AF_LOCAL || addr->sa_family == AF_UNIX)
191     {
192       struct sockaddr_in myaddr;
193       struct sockaddr_un *unaddr;
194       unsigned short port;
195       char nonce[16];
196       int ret;
197       
198       unaddr = (struct sockaddr_un *)addr;
199       if (read_port_and_nonce (unaddr->sun_path, &port, nonce))
200         return -1;
201       
202       myaddr.sin_family = AF_INET;
203       myaddr.sin_port = htons (port); 
204       myaddr.sin_addr.s_addr = htonl (INADDR_LOOPBACK);
205   
206       /* Set return values.  */
207       unaddr->sun_family = myaddr.sin_family;
208       unaddr->sun_port = myaddr.sin_port;
209       unaddr->sun_addr.s_addr = myaddr.sin_addr.s_addr;
210   
211       ret = connect (HANDLE2SOCKET(sockfd), 
212                      (struct sockaddr *)&myaddr, sizeof myaddr);
213       if (!ret)
214         {
215           /* Send the nonce. */
216           ret = _assuan_io_write (sockfd, nonce, 16);
217           if (ret >= 0 && ret != 16)
218             {
219               errno = EIO;
220               ret = -1;
221             }
222         }
223       return ret;
224     }
225   else
226     {
227       int res;
228       res = connect (HANDLE2SOCKET (sockfd), addr, addrlen);
229       if (res < 0)
230         errno = _assuan_sock_wsa2errno (WSAGetLastError ());
231       return res;
232     }      
233 #else
234   return connect (sockfd, addr, addrlen);
235 #endif
236 }
237
238
239 int
240 _assuan_sock_bind (assuan_fd_t sockfd, struct sockaddr *addr, int addrlen)
241 {
242 #ifdef HAVE_W32_SYSTEM
243   if (addr->sa_family == AF_LOCAL || addr->sa_family == AF_UNIX)
244     {
245       struct sockaddr_in myaddr;
246       struct sockaddr_un *unaddr;
247       int filefd;
248       FILE *fp;
249       int len = sizeof myaddr;
250       int rc;
251       char nonce[16];
252
253       if (get_nonce (nonce, 16))
254         return -1;
255
256       unaddr = (struct sockaddr_un *)addr;
257
258       myaddr.sin_port = 0;
259       myaddr.sin_family = AF_INET;
260       myaddr.sin_addr.s_addr = htonl (INADDR_LOOPBACK);
261
262       filefd = open (unaddr->sun_path, 
263                      (O_WRONLY|O_CREAT|O_EXCL|O_BINARY), 
264                      (S_IRUSR|S_IWUSR|S_IRGRP|S_IWGRP));
265       if (filefd == -1)
266         {
267           if (errno == EEXIST)
268             errno = WSAEADDRINUSE;
269           return -1;
270         }
271       fp = fdopen (filefd, "wb");
272       if (!fp)
273         { 
274           int save_e = errno;
275           close (filefd);
276           errno = save_e;
277           return -1;
278         }
279
280       rc = bind (HANDLE2SOCKET (sockfd), (struct sockaddr *)&myaddr, len);
281       if (!rc)
282         rc = getsockname (HANDLE2SOCKET (sockfd), 
283                           (struct sockaddr *)&myaddr, &len);
284       if (rc)
285         {
286           int save_e = errno;
287           fclose (fp);
288           remove (unaddr->sun_path);
289           errno = save_e;
290           return rc;
291         }
292       fprintf (fp, "%d\n", ntohs (myaddr.sin_port));
293       fwrite (nonce, 16, 1, fp);
294       fclose (fp);
295
296       return 0;
297     }
298   else
299     {
300       int res = bind (HANDLE2SOCKET(sockfd), addr, addrlen);
301       if (res < 0)
302         errno = _assuan_sock_wsa2errno (WSAGetLastError ());
303       return res;
304     }
305 #else
306   return bind (sockfd, addr, addrlen);
307 #endif
308 }
309
310
311 int
312 _assuan_sock_get_nonce (struct sockaddr *addr, int addrlen, 
313                         assuan_sock_nonce_t *nonce)
314 {
315 #ifdef HAVE_W32_SYSTEM
316   if (addr->sa_family == AF_LOCAL || addr->sa_family == AF_UNIX)
317     {
318       struct sockaddr_un *unaddr;
319       unsigned short port;
320
321       if (sizeof nonce->nonce != 16)
322         {
323           errno = EINVAL;
324           return -1;
325         }
326       nonce->length = 16;
327       unaddr = (struct sockaddr_un *)addr;
328       if (read_port_and_nonce (unaddr->sun_path, &port, nonce->nonce))
329         return -1;
330     }
331   else
332     {
333       nonce->length = 42; /* Arbitrary valuie to detect unitialized nonce. */
334       nonce->nonce[0] = 42;
335     }
336 #else
337   (void)addr;
338   (void)addrlen;
339   nonce->length = 0;
340 #endif
341   return 0;
342 }
343  
344  
345 int
346 _assuan_sock_check_nonce (assuan_fd_t fd, assuan_sock_nonce_t *nonce)
347 {
348 #ifdef HAVE_W32_SYSTEM
349   char buffer[16], *p;
350   size_t nleft;
351   int n;
352
353   if (sizeof nonce->nonce != 16)
354     {
355       errno = EINVAL;
356       return -1;
357     }
358
359   if (nonce->length == 42 && nonce->nonce[0] == 42)
360     return 0; /* Not a Unix domain socket.  */
361
362   if (nonce->length != 16)
363     {
364       errno = EINVAL;
365       return -1;
366     }
367       
368   p = buffer;
369   nleft = 16;
370   while (nleft)
371     {
372       n = _assuan_io_read (SOCKET2HANDLE(fd), p, nleft);
373       if (n < 0 && errno == EINTR)
374         ;
375       else if (n < 0 && errno == EAGAIN)
376         Sleep (100);
377       else if (n < 0)
378         return -1;
379       else if (!n)
380         {
381           errno = EIO;
382           return -1;
383         }
384       else
385         {
386           p += n;
387           nleft -= n;
388         }
389     }
390   if (memcmp (buffer, nonce->nonce, 16))
391     {
392       errno = EACCES;
393       return -1;
394     }
395 #else
396   (void)fd;
397   (void)nonce;
398 #endif
399   return 0;
400 }
401
402
403 /* Public API.  */
404 int
405 assuan_sock_close (assuan_fd_t fd)
406 {
407   return _assuan_close (fd);
408 }
409
410 assuan_fd_t 
411 assuan_sock_new (int domain, int type, int proto)
412 {
413   return _assuan_sock_new (domain, type, proto);
414 }
415
416 int
417 assuan_sock_connect (assuan_fd_t sockfd, struct sockaddr *addr, int addrlen)
418 {
419   return _assuan_sock_connect (sockfd, addr, addrlen);
420 }
421
422 int
423 assuan_sock_bind (assuan_fd_t sockfd, struct sockaddr *addr, int addrlen)
424 {
425   return _assuan_sock_bind (sockfd, addr, addrlen);
426 }
427
428 int
429 assuan_sock_get_nonce (struct sockaddr *addr, int addrlen, 
430                        assuan_sock_nonce_t *nonce)
431 {     
432   return _assuan_sock_get_nonce (addr, addrlen, nonce);
433 }
434
435 int
436 assuan_sock_check_nonce (assuan_fd_t fd, assuan_sock_nonce_t *nonce)
437 {
438   return _assuan_sock_check_nonce (fd, nonce);
439 }