2 * Copyright (c) 2000 - 2013 Samsung Electronics Co., Ltd. All rights reserved.
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
8 * http://www.apache.org/licenses/LICENSE-2.0
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.
22 #include <sys/types.h>
24 #include <sys/socket.h>
26 #include <arpa/inet.h>
28 #include <netinet/in.h>
29 #include <netinet/tcp.h>
32 #include <systemd/sd-daemon.h>
36 #include "secure_socket.h"
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 */
47 SCHEME_UNKNOWN = 0x02,
50 struct function_table {
52 int (*create_socket)(const char *peer, int port, struct sockaddr *addr);
53 int (*setup_handle)(int handle);
58 static inline int create_unix_socket(const char *peer, int port, struct sockaddr *addr)
62 struct sockaddr_un *un_addr = (struct sockaddr_un *)addr;
64 len = sizeof(*un_addr);
67 if (strlen(peer) >= sizeof(un_addr->sun_path)) {
68 ErrPrint("peer %s is too long to remember it\\n", peer);
72 /* We can believe this has no prob, because
73 * we already check the size of add.rsun_path
75 strcpy(un_addr->sun_path, peer);
76 un_addr->sun_family = AF_UNIX;
78 handle = socket(PF_UNIX, SOCK_STREAM, 0);
81 ErrPrint("Failed to create a socket %s\n", strerror(errno));
87 static inline int create_inet_socket(const char *peer, int port, struct sockaddr *addr)
90 struct sockaddr_in *in_addr = (struct sockaddr_in *)addr;
92 bzero(in_addr, sizeof(*in_addr));
94 in_addr->sin_port = htons(port);
95 in_addr->sin_family = AF_INET;
97 in_addr->sin_addr.s_addr = htonl(INADDR_ANY);
99 in_addr->sin_addr.s_addr = inet_addr(peer);
102 handle = socket(AF_INET, SOCK_STREAM, 0);
105 ErrPrint("socket: %s\n", strerror(errno));
111 static inline int create_systemd_socket(const char *peer, int port, struct sockaddr *addr)
116 cnt = sd_listen_fds(0);
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;
122 handle = create_inet_socket(peer, port, addr);
128 static inline int setup_unix_handle(int handle)
131 int sndbuf = SNDBUF_SZ;
132 int rcvbuf = RCVBUF_SZ;
134 if (setsockopt(handle, SOL_SOCKET, SO_PASSCRED, &on, sizeof(on)) < 0) {
137 ErrPrint("Failed to change sock opt : %s\n", strerror(errno));
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));
148 static inline int setup_inet_handle(int handle)
152 (void)setsockopt(handle, IPPROTO_IP, TCP_NODELAY, &on, sizeof(on));
157 static inline char *parse_scheme(const char *peer, int *port, struct function_table *vtable)
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;
173 ErrPrint("Heap: %s\n", strerror(errno));
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;
184 ErrPrint("Heap: %s\n", strerror(errno));
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)) {
193 vtable->type = (int)SCHEME_REMOTE;
194 peer += COM_CORE_REMOTE_SCHEME_LEN;
196 for (len = 0; peer[len] && peer[len] != ':'; len++);
197 if (peer[len] != ':') {
198 ErrPrint("Invalid syntax: %s\n", peer);
202 addr = malloc(len + 1);
204 ErrPrint("Heap: %s\n", strerror(errno));
209 strncpy(addr, peer, len);
215 *port = strtoul(peer, &endptr, 10);
216 if (*endptr != '\0' || peer == endptr) {
217 ErrPrint("Invalid: %s[%d]\n", peer - len - 1, len + 1);
223 vtable->create_socket = create_inet_socket;
224 vtable->setup_handle = setup_inet_handle;
226 /* Fallback to local scheme */
227 vtable->type = (int)SCHEME_LOCAL;
230 ErrPrint("Heap: %s\n", strerror(errno));
234 vtable->create_socket = create_unix_socket;
235 vtable->setup_handle = setup_unix_handle;
242 EAPI int secure_socket_create_client(const char *peer)
247 struct function_table vtable;
248 struct sockaddr *sockaddr;
249 struct sockaddr_in in_addr;
250 struct sockaddr_un un_addr;
254 addr = parse_scheme(peer, &port, &vtable);
256 ErrPrint("peer: [%s] is not valid\n", peer);
260 switch (vtable.type) {
262 sockaddr = (struct sockaddr *)&un_addr;
263 addrlen = sizeof(un_addr);
266 sockaddr = (struct sockaddr *)&in_addr;
267 addrlen = sizeof(in_addr);
274 handle = vtable.create_socket(addr, port, sockaddr);
280 ret = connect(handle, sockaddr, addrlen);
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));
292 ret = vtable.setup_handle(handle);
294 if (close(handle) < 0) {
295 ErrPrint("close: %s\n", strerror(errno));
304 EAPI int secure_socket_create_server(const char *peer)
310 struct sockaddr *sockaddr;
311 struct sockaddr_in in_addr;
312 struct sockaddr_un un_addr;
313 struct function_table vtable;
316 addr = parse_scheme(peer, &port, &vtable);
318 ErrPrint("Failed to parse scheme\n");
322 switch (vtable.type) {
324 sockaddr = (struct sockaddr *)&un_addr;
325 addrlen = sizeof(un_addr);
328 sockaddr = (struct sockaddr *)&in_addr;
329 addrlen = sizeof(in_addr);
336 handle = vtable.create_socket(addr, port, sockaddr);
342 ret = bind(handle, sockaddr, addrlen);
345 ErrPrint("bind: %s\n", strerror(errno));
346 if (close(handle) < 0) {
347 ErrPrint("close: %s\n", strerror(errno));
352 ret = listen(handle, BACKLOG);
355 ErrPrint("listen: %s\n", strerror(errno));
356 if (close(handle) < 0) {
357 ErrPrint("close: %s\n", strerror(errno));
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));
371 EAPI int secure_socket_get_connection_handle(int server_handle)
373 struct sockaddr_in in_addr;
374 struct sockaddr_un un_addr;
375 struct sockaddr *addr;
378 socklen_t size = sizeof(un_addr);
380 /* Finding the largest buffer */
381 if (sizeof(in_addr) > sizeof(un_addr)) {
382 addr = (struct sockaddr *)&in_addr;
383 size = sizeof(in_addr);
385 addr = (struct sockaddr *)&un_addr;
386 size = sizeof(un_addr);
389 handle = accept(server_handle, addr, &size);
392 ErrPrint("Failed to accept a new client %s\n", strerror(errno));
396 if (addr->sa_family == AF_UNIX) {
397 ret = setup_unix_handle(handle);
399 if (close(handle) < 0) {
400 ErrPrint("close: %s\n", strerror(errno));
405 } else if (addr->sa_family == AF_INET) {
406 ret = setup_inet_handle(handle);
408 if (close(handle) < 0) {
409 ErrPrint("close: %s\n", strerror(errno));
415 ErrPrint("Unknown address family: %d\n", addr->sa_family);
421 EAPI int secure_socket_send_with_fd(int handle, const char *buffer, int size, int fd)
427 char control[CMSG_SPACE(sizeof(int))];
431 if (!buffer || size <= 0) {
432 ErrPrint("Reject: 0 byte data sending\n");
436 memset(&msg, 0, sizeof(msg));
437 iov.iov_base = (char *)buffer;
445 struct cmsghdr *cmsg;
448 msg.msg_control = cmsgu.control;
449 msg.msg_controllen = sizeof(cmsgu.control);
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);
459 ret = sendmsg(handle, &msg, 0);
462 if (errno == EAGAIN || errno == EWOULDBLOCK) {
463 ErrPrint("handle[%d] size[%d] Try again [%s]\n", handle, size, strerror(errno));
466 ErrPrint("Failed to send message [%s]\n", strerror(errno));
473 EAPI int secure_socket_send(int handle, const char *buffer, int size)
475 return secure_socket_send_with_fd(handle, buffer, size, -1);
478 EAPI int secure_socket_recv_with_fd(int handle, char *buffer, int size, int *sender_pid, int *fd)
481 struct cmsghdr *cmsg;
487 if (size <= 0 || !buffer) {
495 memset(&msg, 0, sizeof(msg));
496 iov.iov_base = buffer;
500 msg.msg_control = control;
501 msg.msg_controllen = sizeof(control);
503 ret = recvmsg(handle, &msg, 0);
506 DbgPrint("Disconnected\n");
512 if (errno == EAGAIN || errno == EWOULDBLOCK) {
513 ErrPrint("handle[%d] size[%d] Try again [%s]\n", handle, size, strerror(errno));
517 ErrPrint("Failed to recvmsg [%s]\n", strerror(errno));
521 *sender_pid = -1; /* In case of remote socket, cannot delivery this */
522 cmsg = CMSG_FIRSTHDR(&msg);
524 if (cmsg->cmsg_level == SOL_SOCKET && cmsg->cmsg_type == SCM_CREDENTIALS) {
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) {
531 cdata = (int *)CMSG_DATA(cmsg);
535 cmsg = CMSG_NXTHDR(&msg, cmsg);
541 EAPI int secure_socket_recv(int handle, char *buffer, int size, int *sender_pid)
543 return secure_socket_recv_with_fd(handle, buffer, size, sender_pid, NULL);
546 EAPI int secure_socket_destroy_handle(int handle)
548 if (close(handle) < 0) {
551 ErrPrint("close: %s\n", strerror(errno));