a299b50433c5e1b2e04501d4636c89b90cee60cd
[framework/appfw/com-core.git] / src / secure_socket.c
1 /*
2  * Copyright (c) 2000 - 2013 Samsung Electronics Co., Ltd. All rights reserved.
3  *
4  * Licensed under the Apache License, Version 2.0 (the "License");
5  * you may not use this file except in compliance with the License.
6  * You may obtain a copy of the License at
7  *
8  * http://www.apache.org/licenses/LICENSE-2.0
9  *
10  * Unless required by applicable law or agreed to in writing, software
11  * distributed under the License is distributed on an "AS IS" BASIS,
12  * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
13  * See the License for the specific language governing permissions and
14  * limitations under the License.
15  *
16 */
17
18 #define _GNU_SOURCE
19 #include <stdio.h>
20 #include <unistd.h>
21
22 #include <sys/types.h>
23 #include <sys/stat.h>
24 #include <sys/socket.h>
25 #include <netdb.h>
26 #include <arpa/inet.h>
27 #include <sys/un.h>
28 #include <netinet/in.h>
29 #include <netinet/tcp.h>
30 #include <errno.h>
31 #include <stdlib.h>
32 #include <systemd/sd-daemon.h>
33
34 #include <dlog.h>
35
36 #include "secure_socket.h"
37 #include "debug.h"
38 #include "util.h"
39
40 #define BACKLOG         50      /*!< Accept only 50 connections as default */
41 #define SNDBUF_SZ       262144  /*!< 256 KB, this will be doubled by kernel */
42 #define RCVBUF_SZ       524288  /*!< 512 KB, this will be doubled by kernel */
43
44 enum scheme {
45     SCHEME_LOCAL = 0x00,
46     SCHEME_REMOTE = 0x01,
47     SCHEME_UNKNOWN = 0x02,
48 };
49
50 struct function_table {
51     int type;
52     int (*create_socket)(const char *peer, int port, struct sockaddr *addr);
53     int (*setup_handle)(int handle);
54 };
55
56 int errno;
57
58 static inline int create_unix_socket(const char *peer, int port, struct sockaddr *addr)
59 {
60     int len;
61     int handle;
62     struct sockaddr_un *un_addr = (struct sockaddr_un *)addr;
63
64     len = sizeof(*un_addr);
65     bzero(un_addr, len);
66
67     if (strlen(peer) >= sizeof(un_addr->sun_path)) {
68         ErrPrint("peer %s is too long to remember it\\n", peer);
69         return -1;
70     }
71
72     /* We can believe this has no prob, because
73      * we already check the size of add.rsun_path
74      */
75     strcpy(un_addr->sun_path, peer);
76     un_addr->sun_family = AF_UNIX;
77
78     handle = socket(PF_UNIX, SOCK_STREAM, 0);
79     if (handle < 0) {
80         handle = -errno;
81         ErrPrint("Failed to create a socket %s\n", strerror(errno));
82     }
83
84     return handle;
85 }
86
87 static inline int create_inet_socket(const char *peer, int port, struct sockaddr *addr)
88 {
89     int handle;
90     struct sockaddr_in *in_addr = (struct sockaddr_in *)addr;
91
92     bzero(in_addr, sizeof(*in_addr));
93
94     in_addr->sin_port = htons(port);
95     in_addr->sin_family = AF_INET;
96     if (*peer == '\0') {
97         in_addr->sin_addr.s_addr = htonl(INADDR_ANY);
98     } else {
99         in_addr->sin_addr.s_addr = inet_addr(peer);
100     }
101
102     handle = socket(AF_INET, SOCK_STREAM, 0);
103     if (handle < 0) {
104         handle = -errno;
105         ErrPrint("socket: %s\n", strerror(errno));
106     }
107
108     return handle;
109 }
110
111 static inline int create_systemd_socket(const char *peer, int port, struct sockaddr *addr)
112 {
113     int handle = -1;
114     int cnt;
115
116     cnt = sd_listen_fds(0);
117     if (cnt > 1) {
118         ErrPrint("To many file descriptors are received on socket activation: %d\n", cnt);
119     } else if (cnt == 1) {
120         handle = SD_LISTEN_FDS_START + 0;
121     } else {
122         handle = create_inet_socket(peer, port, addr);
123     }
124
125     return handle;
126 }
127
128 static inline int setup_unix_handle(int handle)
129 {
130     int on = 1;
131     int sndbuf = SNDBUF_SZ;
132     int rcvbuf = RCVBUF_SZ;
133
134     if (setsockopt(handle, SOL_SOCKET, SO_PASSCRED, &on, sizeof(on)) < 0) {
135         int ret;
136         ret = -errno;
137         ErrPrint("Failed to change sock opt : %s\n", strerror(errno));
138         return ret;
139     }
140
141     (void)setsockopt(handle, SOL_SOCKET, SO_RCVBUF, &rcvbuf, sizeof(rcvbuf));
142     (void)setsockopt(handle, SOL_SOCKET, SO_SNDBUF, &sndbuf, sizeof(sndbuf));
143     (void)setsockopt(handle, IPPROTO_IP, TCP_NODELAY, &on, sizeof(on));
144
145     return 0;
146 }
147
148 static inline int setup_inet_handle(int handle)
149 {
150     int on = 1;
151
152     (void)setsockopt(handle, IPPROTO_IP, TCP_NODELAY, &on, sizeof(on));
153
154     return 0;
155 }
156
157 static inline char *parse_scheme(const char *peer, int *port, struct function_table *vtable)
158 {
159     int _port;
160     char *addr = NULL;
161
162     if (!port)
163         port = &_port;
164
165     *port = 0;
166
167     if (!strncasecmp(peer, COM_CORE_LOCAL_SCHEME, COM_CORE_LOCAL_SCHEME_LEN)) {
168         vtable->type = (int)SCHEME_LOCAL;
169         peer += COM_CORE_LOCAL_SCHEME_LEN;
170
171         addr = strdup(peer);
172         if (!addr) {
173             ErrPrint("Heap: %s\n", strerror(errno));
174         }
175
176         vtable->create_socket = create_unix_socket;
177         vtable->setup_handle = setup_unix_handle;
178     } else if (!strncasecmp(peer, COM_CORE_SD_LOCAL_SCHEME, COM_CORE_SD_LOCAL_SCHEME_LEN)) {
179         vtable->type = (int)SCHEME_LOCAL;
180         peer += COM_CORE_SD_LOCAL_SCHEME_LEN;
181
182         addr = strdup(peer);
183         if (!addr) {
184             ErrPrint("Heap: %s\n", strerror(errno));
185         }
186
187         vtable->create_socket = create_systemd_socket;
188         vtable->setup_handle = setup_unix_handle;
189     } else if (!strncasecmp(peer, COM_CORE_REMOTE_SCHEME, COM_CORE_REMOTE_SCHEME_LEN)) {
190         register int len;
191         char *endptr;
192
193         vtable->type = (int)SCHEME_REMOTE;
194         peer += COM_CORE_REMOTE_SCHEME_LEN;
195
196         for (len = 0; peer[len] && peer[len] != ':'; len++);
197         if (peer[len] != ':') {
198             ErrPrint("Invalid syntax: %s\n", peer);
199             goto out;
200         }
201
202         addr = malloc(len + 1);
203         if (!addr) {
204             ErrPrint("Heap: %s\n", strerror(errno));
205             goto out;
206         }
207
208         if (len > 0) {
209             strncpy(addr, peer, len);
210         }
211
212         addr[len] = '\0';
213
214         peer += len + 1;
215         *port = strtoul(peer, &endptr, 10);
216         if (*endptr != '\0' || peer == endptr) {
217             ErrPrint("Invalid: %s[%d]\n", peer - len - 1, len + 1);
218             free(addr);
219             addr = NULL;
220             goto out;
221         }
222
223         vtable->create_socket = create_inet_socket;
224         vtable->setup_handle = setup_inet_handle;
225     } else {
226         /* Fallback to local scheme */
227         vtable->type = (int)SCHEME_LOCAL;
228         addr = strdup(peer);
229         if (!addr) {
230             ErrPrint("Heap: %s\n", strerror(errno));
231             goto out;
232         }
233
234         vtable->create_socket = create_unix_socket;
235         vtable->setup_handle = setup_unix_handle;
236     }
237
238 out:
239     return addr;
240 }
241
242 EAPI int secure_socket_create_client(const char *peer)
243 {
244     int port;
245     char *addr;
246     int ret;
247     struct function_table vtable;
248     struct sockaddr *sockaddr;
249     struct sockaddr_in in_addr;
250     struct sockaddr_un un_addr;
251     int handle;
252     int addrlen;
253
254     addr = parse_scheme(peer, &port, &vtable);
255     if (!addr) {
256         ErrPrint("peer: [%s] is not valid\n", peer);
257         return -EINVAL;
258     }
259
260     switch (vtable.type) {
261         case SCHEME_LOCAL:
262             sockaddr = (struct sockaddr *)&un_addr;
263             addrlen = sizeof(un_addr);
264             break;
265         case SCHEME_REMOTE:
266             sockaddr = (struct sockaddr *)&in_addr;
267             addrlen = sizeof(in_addr);
268             break;
269         default:
270             free(addr);
271             return -EINVAL;
272     }
273
274     handle = vtable.create_socket(addr, port, sockaddr);
275     free(addr);
276     if (handle < 0) {
277         return handle;
278     }
279
280     ret = connect(handle, sockaddr, addrlen);
281     if (ret < 0) {
282         ret = -errno;
283         ErrPrint("Failed to connect to server [%s] %s\n",
284                 peer, strerror(errno));
285         if (close(handle) < 0) {
286             ErrPrint("close: %s\n", strerror(errno));
287         }
288
289         return ret;
290     }
291
292     ret = vtable.setup_handle(handle);
293     if (ret < 0) {
294         if (close(handle) < 0) {
295             ErrPrint("close: %s\n", strerror(errno));
296         }
297
298         return ret;
299     }
300
301     return handle;
302 }
303
304 EAPI int secure_socket_create_server(const char *peer)
305 {
306     int port;
307     char *addr;
308     int handle;
309     int ret;
310     struct sockaddr *sockaddr;
311     struct sockaddr_in in_addr;
312     struct sockaddr_un un_addr;
313     struct function_table vtable;
314     int addrlen;
315
316     addr = parse_scheme(peer, &port, &vtable);
317     if (!addr) {
318         ErrPrint("Failed to parse scheme\n");
319         return -EINVAL;
320     }
321
322     switch (vtable.type) {
323         case SCHEME_LOCAL:
324             sockaddr = (struct sockaddr *)&un_addr;
325             addrlen = sizeof(un_addr);
326             break;
327         case SCHEME_REMOTE:
328             sockaddr = (struct sockaddr *)&in_addr;
329             addrlen = sizeof(in_addr);
330             break;
331         default:
332             free(addr);
333             return -EINVAL;
334     }
335
336     handle = vtable.create_socket(addr, port, sockaddr);
337     free(addr);
338     if (handle < 0) {
339         return handle;
340     }
341
342     ret = bind(handle, sockaddr, addrlen);
343     if (ret < 0) {
344         ret = -errno;
345         ErrPrint("bind: %s\n", strerror(errno));
346         if (close(handle) < 0) {
347             ErrPrint("close: %s\n", strerror(errno));
348         }
349         return ret;
350     }
351
352     ret = listen(handle, BACKLOG);
353     if (ret < 0) {
354         ret = -errno;
355         ErrPrint("listen: %s\n", strerror(errno));
356         if (close(handle) < 0) {
357             ErrPrint("close: %s\n", strerror(errno));
358         }
359         return ret;
360     }
361
362     if (vtable.type == SCHEME_LOCAL) {
363         if (chmod(peer, 0666) < 0) {
364             ErrPrint("Failed to change the permission of a socket (%s)\n", strerror(errno));
365         }
366     }
367
368     return handle;
369 }
370
371 EAPI int secure_socket_get_connection_handle(int server_handle)
372 {
373     struct sockaddr_in in_addr;
374     struct sockaddr_un un_addr;
375     struct sockaddr *addr;
376     int handle;
377     int ret;
378     socklen_t size = sizeof(un_addr);
379
380     /* Finding the largest buffer */
381     if (sizeof(in_addr) > sizeof(un_addr)) {
382         addr = (struct sockaddr *)&in_addr;
383         size = sizeof(in_addr);
384     } else {
385         addr = (struct sockaddr *)&un_addr;
386         size = sizeof(un_addr);
387     }
388
389     handle = accept(server_handle, addr, &size);
390     if (handle < 0) {
391         ret = -errno;
392         ErrPrint("Failed to accept a new client %s\n", strerror(errno));
393         return ret;
394     }
395
396     if (addr->sa_family == AF_UNIX) {
397         ret = setup_unix_handle(handle);
398         if (ret < 0) {
399             if (close(handle) < 0) {
400                 ErrPrint("close: %s\n", strerror(errno));
401             }
402
403             handle = ret;
404         }
405     } else if (addr->sa_family == AF_INET) {
406         ret = setup_inet_handle(handle);
407         if (ret < 0) {
408             if (close(handle) < 0) {
409                 ErrPrint("close: %s\n", strerror(errno));
410             }
411
412             handle = ret;
413         }
414     } else {
415         ErrPrint("Unknown address family: %d\n", addr->sa_family);
416     }
417
418     return handle;
419 }
420
421 EAPI int secure_socket_send_with_fd(int handle, const char *buffer, int size, int fd)
422 {
423     struct msghdr msg;
424     struct iovec iov;
425     union {
426         struct cmsghdr hdr;
427         char control[CMSG_SPACE(sizeof(int))];
428     } cmsgu;
429     int ret;
430
431     if (!buffer || size <= 0) {
432         ErrPrint("Reject: 0 byte data sending\n");
433         return -EINVAL;
434     }
435
436     memset(&msg, 0, sizeof(msg));
437     iov.iov_base = (char *)buffer;
438     iov.iov_len = size;
439     msg.msg_name = NULL;
440     msg.msg_namelen = 0;
441     msg.msg_iov = &iov;
442     msg.msg_iovlen = 1;
443
444     if (fd >= 0) {
445         struct cmsghdr *cmsg;
446         int *cdata;
447
448         msg.msg_control = cmsgu.control;
449         msg.msg_controllen = sizeof(cmsgu.control);
450
451         cmsg = CMSG_FIRSTHDR(&msg);
452         cmsg->cmsg_len = CMSG_LEN(sizeof(fd));
453         cmsg->cmsg_level = SOL_SOCKET;
454         cmsg->cmsg_type = SCM_RIGHTS;
455         cdata = (int *)CMSG_DATA(cmsg);
456         *cdata = fd;
457     }
458
459     ret = sendmsg(handle, &msg, 0);
460     if (ret < 0) {
461         ret = -errno;
462         if (errno == EAGAIN || errno == EWOULDBLOCK) {
463             ErrPrint("handle[%d] size[%d] Try again [%s]\n", handle, size, strerror(errno));
464             return -EAGAIN;
465         }
466         ErrPrint("Failed to send message [%s]\n", strerror(errno));
467         return ret;
468     }
469
470     return iov.iov_len;
471 }
472
473 EAPI int secure_socket_send(int handle, const char *buffer, int size)
474 {
475     return secure_socket_send_with_fd(handle, buffer, size, -1);
476 }
477
478 EAPI int secure_socket_recv_with_fd(int handle, char *buffer, int size, int *sender_pid, int *fd)
479 {
480     struct msghdr msg;
481     struct cmsghdr *cmsg;
482     struct iovec iov;
483     char control[1024];
484     int _pid;
485     int ret;
486
487     if (size <= 0 || !buffer) {
488         return -EINVAL;
489     }
490
491     if (!sender_pid) {
492         sender_pid = &_pid;
493     }
494
495     memset(&msg, 0, sizeof(msg));
496     iov.iov_base = buffer;
497     iov.iov_len = size;
498     msg.msg_iov = &iov;
499     msg.msg_iovlen = 1;
500     msg.msg_control = control;
501     msg.msg_controllen = sizeof(control);
502
503     ret = recvmsg(handle, &msg, 0);
504     if (ret == 0) {
505         /*!< Disconnected */
506         DbgPrint("Disconnected\n");
507         return 0;
508     }
509
510     if (ret < 0) {
511         ret = -errno;
512         if (errno == EAGAIN || errno == EWOULDBLOCK) {
513             ErrPrint("handle[%d] size[%d] Try again [%s]\n", handle, size, strerror(errno));
514             return -EAGAIN;
515         }
516
517         ErrPrint("Failed to recvmsg [%s]\n", strerror(errno));
518         return ret;
519     }
520
521     *sender_pid = -1;   /* In case of remote socket, cannot delivery this */ 
522     cmsg = CMSG_FIRSTHDR(&msg);
523     while (cmsg) {
524         if (cmsg->cmsg_level == SOL_SOCKET && cmsg->cmsg_type == SCM_CREDENTIALS) {
525             struct ucred *cred;
526
527             cred = (struct ucred *)CMSG_DATA(cmsg);
528             *sender_pid = cred->pid;
529         } else if (fd && cmsg->cmsg_level == SOL_SOCKET && cmsg->cmsg_type == SCM_RIGHTS) {
530             int *cdata;
531             cdata = (int *)CMSG_DATA(cmsg);
532             *fd = *cdata;
533         }
534
535         cmsg = CMSG_NXTHDR(&msg, cmsg);
536     }
537
538     return iov.iov_len;
539 }
540
541 EAPI int secure_socket_recv(int handle, char *buffer, int size, int *sender_pid)
542 {
543     return secure_socket_recv_with_fd(handle, buffer, size, sender_pid, NULL);
544 }
545
546 EAPI int secure_socket_destroy_handle(int handle)
547 {
548     if (close(handle) < 0) {
549         int ret;
550         ret = -errno;
551         ErrPrint("close: %s\n", strerror(errno));
552         return ret;
553     }
554
555     return 0;
556 }
557
558 #undef _GNU_SOURCE
559
560 /* End of a file */