Blob


1 /*
2 * Copyright (c) 2021 Omar Polo <op@omarpolo.com>
3 *
4 * Permission to use, copy, modify, and distribute this software for any
5 * purpose with or without fee is hereby granted, provided that the above
6 * copyright notice and this permission notice appear in all copies.
7 *
8 * THE SOFTWARE IS PROVIDED "AS IS" AND THE AUTHOR DISCLAIMS ALL WARRANTIES
9 * WITH REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF
10 * MERCHANTABILITY AND FITNESS. IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR
11 * ANY SPECIAL, DIRECT, INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES
12 * WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR PROFITS, WHETHER IN AN
13 * ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING OUT OF
14 * OR IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE.
15 */
17 #include "compat.h"
19 #include <errno.h>
20 #include <pwd.h>
21 #include <signal.h>
22 #include <stdlib.h>
23 #include <string.h>
24 #include <syslog.h>
25 #include <unistd.h>
27 #include "client.h"
28 #include "kamid.h"
29 #include "log.h"
30 #include "sandbox.h"
32 static struct imsgev *iev_listener;
34 static __dead void client_shutdown(void);
35 static void client_sig_handler(int, short, void *);
36 static void client_dispatch_listener(int, short, void *);
37 static void client_privdrop(const char *, const char *);
39 static int client_imsg_compose_listener(int, uint32_t,
40 const void *, uint16_t);
42 __dead void
43 client(int debug, int verbose)
44 {
45 struct event ev_sigint, ev_sigterm;
47 log_init(debug, LOG_DAEMON);
48 log_setverbose(verbose);
50 setproctitle("client");
51 log_procinit("client");
53 log_debug("warming up");
55 event_init();
57 /* Setup signal handlers */
58 signal_set(&ev_sigint, SIGINT, client_sig_handler, NULL);
59 signal_set(&ev_sigterm, SIGTERM, client_sig_handler, NULL);
61 signal_add(&ev_sigint, NULL);
62 signal_add(&ev_sigterm, NULL);
64 signal(SIGPIPE, SIG_IGN);
65 signal(SIGHUP, SIG_IGN);
67 /* Setup pipe and event handler to the listener process */
68 if ((iev_listener = malloc(sizeof(*iev_listener))) == NULL)
69 fatal(NULL);
71 imsg_init(&iev_listener->ibuf, 3);
72 iev_listener->handler = client_dispatch_listener;
74 /* Setup event handlers. */
75 iev_listener->events = EV_READ;
76 event_set(&iev_listener->ev, iev_listener->ibuf.fd,
77 iev_listener->events, iev_listener->handler, iev_listener);
78 event_add(&iev_listener->ev, NULL);
80 log_debug("before dispatch");
81 event_dispatch();
82 client_shutdown();
83 }
85 static __dead void
86 client_shutdown(void)
87 {
88 msgbuf_clear(&iev_listener->ibuf.w);
89 close(iev_listener->ibuf.fd);
91 free(iev_listener);
93 log_info("client exiting");
94 exit(0);
95 }
97 static void
98 client_sig_handler(int sig, short event, void *d)
99 {
100 /*
101 * Normal signal handler rules don't apply because libevent
102 * decouples for us.
103 */
105 switch (sig) {
106 case SIGINT:
107 case SIGTERM:
108 client_shutdown();
109 default:
110 fatalx("unexpected signal %d", sig);
114 #define AUTH_NONE 0
115 #define AUTH_USER 1
116 #define AUTH_DONE 2
118 static void
119 client_dispatch_listener(int fd, short event, void *d)
121 static int auth = AUTH_NONE;
122 static char username[64] = {0};
123 static char dir[PATH_MAX] = {0};
124 struct imsg imsg;
125 struct imsgev *iev = d;
126 struct imsgbuf *ibuf;
127 ssize_t n;
128 int shut = 0;
130 ibuf = &iev->ibuf;
132 if (event & EV_READ) {
133 if ((n = imsg_read(ibuf)) == -1 && errno != EAGAIN)
134 fatal("imsg_read error");
135 if (n == 0) /* Connection closed */
136 shut = 1;
138 if (event & EV_WRITE) {
139 if ((n = msgbuf_write(&ibuf->w)) == -1 && errno != EAGAIN)
140 fatal("msgbuf_write");
141 if (n == 0) /* Connection closed */
142 shut = 1;
145 for (;;) {
146 if ((n = imsg_get(ibuf, &imsg)) == -1)
147 fatal("%s: imsg_get error", __func__);
148 if (n == 0) /* No more messages. */
149 break;
151 log_debug("client: got message type %d", imsg.hdr.type);
152 switch (imsg.hdr.type) {
153 case IMSG_AUTH:
154 if (auth)
155 fatalx("%s: IMSG_AUTH already done", __func__);
156 auth = AUTH_USER;
157 ((char *)imsg.data)[IMSG_DATA_SIZE(imsg)-1] = '\0';
158 strlcpy(username, imsg.data, sizeof(username));
159 break;
160 case IMSG_AUTH_DIR:
161 if (auth != AUTH_USER)
162 fatalx("%s: IMSG_AUTH_DIR not after IMSG_AUTH",
163 __func__);
164 auth = AUTH_DONE;
165 ((char *)imsg.data)[IMSG_DATA_SIZE(imsg)-1] = '\0';
166 strlcpy(dir, imsg.data, sizeof(dir));
167 client_privdrop(username, dir);
168 memset(username, 0, sizeof(username));
169 memset(dir, 0, sizeof(username));
170 break;
171 case IMSG_BUF:
172 /* echo! */
173 client_imsg_compose_listener(IMSG_BUF, imsg.hdr.peerid,
174 imsg.data, IMSG_DATA_SIZE(imsg));
175 break;
176 case IMSG_CONN_GONE:
177 log_debug("closing");
178 shut = 1;
179 break;
180 default:
181 log_debug("%s: unexpected imsg %d",
182 __func__, imsg.hdr.type);
183 break;
185 imsg_free(&imsg);
188 if (!shut)
189 imsg_event_add(iev);
190 else {
191 /* This pipe is dead. Remove its event handler. */
192 event_del(&iev->ev);
193 log_warnx("pipe closed, shutting down...");
194 event_loopexit(NULL);
198 static void
199 client_privdrop(const char *username, const char *dir)
201 struct passwd *pw;
203 setproctitle("client %s", username);
205 if ((pw = getpwnam(username)) == NULL)
206 fatalx("getpwnam(%s) failed", username);
208 if (chroot(dir) == -1)
209 fatal("chroot");
210 if (chdir("/") == -1)
211 fatal("chdir(\"/\")");
213 if (setgroups(1, &pw->pw_gid) ||
214 setresgid(pw->pw_gid, pw->pw_gid, pw->pw_gid) ||
215 setresuid(pw->pw_uid, pw->pw_uid, pw->pw_uid))
216 fatal("can't drop privileges");
218 sandbox_client();
219 log_debug("client ready");
222 static int
223 client_imsg_compose_listener(int type, uint32_t peerid,
224 const void *data, uint16_t len)
226 int ret;
228 if ((ret = imsg_compose(&iev_listener->ibuf, type, peerid, 0, -1,
229 data, len)) != -1)
230 imsg_event_add(iev_listener);
232 return ret;