xref: /trueos/libexec/launchproxy/launchproxy.c (revision 7a3fc86901ab664f030ee3700af588808dfa8dc2)
1 /*
2  * Copyright (c) 2005 Apple Computer, Inc. All rights reserved.
3  *
4  * @APPLE_APACHE_LICENSE_HEADER_START@
5  *
6  * Licensed under the Apache License, Version 2.0 (the "License");
7  * you may not use this file except in compliance with the License.
8  * You may obtain a copy of the License at
9  *
10  *     http://www.apache.org/licenses/LICENSE-2.0
11  *
12  * Unless required by applicable law or agreed to in writing, software
13  * distributed under the License is distributed on an "AS IS" BASIS,
14  * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
15  * See the License for the specific language governing permissions and
16  * limitations under the License.
17  *
18  * @APPLE_APACHE_LICENSE_HEADER_END@
19  */
20 #include <sys/types.h>
21 #include <sys/select.h>
22 #include <sys/event.h>
23 #include <sys/socket.h>
24 #include <sys/time.h>
25 #include <stdio.h>
26 #include <stdlib.h>
27 #include <stdbool.h>
28 #include <unistd.h>
29 #include <string.h>
30 #include <errno.h>
31 #include <fcntl.h>
32 #include <syslog.h>
33 #include <libgen.h>
34 #include <getopt.h>
35 #include <signal.h>
36 #include <netdb.h>
37 
38 #if !TARGET_OS_EMBEDDED
39 #include <bsm/audit.h>
40 #include <bsm/audit_session.h>
41 #endif // !TARGET_OS_EMBEDDED
42 
43 #include "launch.h"
44 
45 static int kq = 0;
46 
find_fds(launch_data_t o,const char * key,void * context)47 static void find_fds(launch_data_t o, const char *key __attribute__((unused)), void *context __attribute__((unused)))
48 {
49 	struct kevent kev;
50 	size_t i;
51 	int fd;
52 
53 	switch (launch_data_get_type(o)) {
54 	case LAUNCH_DATA_FD:
55 		fd = launch_data_get_fd(o);
56 		if (-1 == fd)
57 			break;
58 		fcntl(fd, F_SETFD, 1);
59 		EV_SET(&kev, fd, EVFILT_READ, EV_ADD, 0, 0, NULL);
60 		if (kevent(kq, &kev, 1, NULL, 0, NULL) == -1)
61 			syslog(LOG_DEBUG, "kevent(%d): %m", fd);
62 		break;
63 	case LAUNCH_DATA_ARRAY:
64 		for (i = 0; i < launch_data_array_get_count(o); i++)
65 			find_fds(launch_data_array_get_index(o, i), NULL, NULL);
66 		break;
67 	case LAUNCH_DATA_DICTIONARY:
68 		launch_data_dict_iterate(o, find_fds, NULL);
69 		break;
70 	default:
71 		break;
72 	}
73 }
74 
main(int argc,char * argv[])75 int main(int argc __attribute__((unused)), char *argv[])
76 {
77 	struct timespec timeout = { 10, 0 };
78 	struct sockaddr_storage ss;
79 	socklen_t slen = (socklen_t)sizeof ss;
80 	struct kevent kev;
81 	int r, ec = EXIT_FAILURE;
82 	launch_data_t tmp, resp, msg = launch_data_alloc(LAUNCH_DATA_STRING);
83 	const char *prog = argv[1];
84 	bool w = false, dupstdout = true, dupstderr = true;
85 
86 	launch_data_set_string(msg, LAUNCH_KEY_CHECKIN);
87 
88 	openlog(getprogname(), LOG_PERROR|LOG_PID|LOG_CONS, LOG_LAUNCHD);
89 
90 	kq = kqueue();
91 
92 	if ((resp = launch_msg(msg)) == NULL) {
93 		syslog(LOG_ERR, "launch_msg(%s): %m", LAUNCH_KEY_CHECKIN);
94 		goto out;
95 	}
96 
97 	launch_data_free(msg);
98 
99 	tmp = launch_data_dict_lookup(resp, LAUNCH_JOBKEY_SOCKETS);
100 	if (tmp) {
101 		find_fds(tmp, NULL, NULL);
102 	} else {
103 		syslog(LOG_ERR, "No FDs found to answer requests on!");
104 		goto out;
105 	}
106 
107 	tmp = launch_data_dict_lookup(resp, LAUNCH_JOBKEY_TIMEOUT);
108 	if (tmp)
109 		timeout.tv_sec = (int)launch_data_get_integer(tmp);
110 
111 	tmp = launch_data_dict_lookup(resp, LAUNCH_JOBKEY_PROGRAM);
112 	if (tmp)
113 		prog = launch_data_get_string(tmp);
114 
115 	tmp = launch_data_dict_lookup(resp, LAUNCH_JOBKEY_INETDCOMPATIBILITY);
116 	if (tmp) {
117 		tmp = launch_data_dict_lookup(tmp, LAUNCH_JOBINETDCOMPATIBILITY_WAIT);
118 		if (tmp)
119 			w = launch_data_get_bool(tmp);
120 	}
121 
122 	if (launch_data_dict_lookup(resp, LAUNCH_JOBKEY_STANDARDOUTPATH))
123 		dupstdout = false;
124 
125 	if (launch_data_dict_lookup(resp, LAUNCH_JOBKEY_STANDARDERRORPATH))
126 		dupstderr = false;
127 
128 	if (!w)
129 		signal(SIGCHLD, SIG_IGN);
130 
131 	for (;;) {
132 		if ((r = kevent(kq, NULL, 0, &kev, 1, &timeout)) == -1) {
133 			syslog(LOG_DEBUG, "kevent(): %m");
134 			goto out;
135 		} else if (r == 0) {
136 			ec = EXIT_SUCCESS;
137 			goto out;
138 		}
139 
140 		if (w) {
141 			dup2((int)kev.ident, STDIN_FILENO);
142 			if (dupstdout)
143 				dup2((int)kev.ident, STDOUT_FILENO);
144 			if (dupstderr)
145 				dup2((int)kev.ident, STDERR_FILENO);
146 			execv(prog, argv + 1);
147 			syslog(LOG_ERR, "execv(): %m");
148 			exit(EXIT_FAILURE);
149 		}
150 
151 		if ((r = accept((int)kev.ident, (struct sockaddr *)&ss, &slen)) == -1) {
152 			if (errno == EWOULDBLOCK)
153 				continue;
154 			syslog(LOG_WARNING, "accept(): %m");
155 			goto out;
156 		} else {
157 			if (ss.ss_family == AF_INET || ss.ss_family == AF_INET6) {
158 				char fromhost[NI_MAXHOST];
159 				char fromport[NI_MAXSERV];
160 				int gni_r;
161 
162 				gni_r = getnameinfo((struct sockaddr *)&ss, slen,
163 						fromhost, (socklen_t) sizeof fromhost,
164 						fromport, (socklen_t) sizeof fromport,
165 						NI_NUMERICHOST | NI_NUMERICSERV);
166 
167 				if (gni_r) {
168 					syslog(LOG_WARNING, "%s: getnameinfo(): %s", prog, gai_strerror(gni_r));
169 				} else {
170 					syslog(LOG_INFO, "%s: Connection from: %s on port: %s", prog, fromhost, fromport);
171 				}
172 			} else {
173 				syslog(LOG_WARNING, "%s: getnameinfo() only supports IPv4/IPv6. Connection from address family: %u", prog, ss.ss_family);
174 			}
175 
176 			switch (fork()) {
177 			case -1:
178 				syslog(LOG_WARNING, "fork(): %m");
179 				if (errno != ENOMEM) {
180 					continue;
181 				}
182 				goto out;
183 			case 0:
184 				break;
185 			default:
186 				close(r);
187 				continue;
188 			}
189 
190 			setpgid(0, 0);
191 
192 #if !TARGET_OS_EMBEDDED
193 			if ((tmp = launch_data_dict_lookup(resp, LAUNCH_JOBKEY_SESSIONCREATE)) && launch_data_get_bool(tmp)) {
194 				auditinfo_addr_t auinfo = {
195 					.ai_termid = { .at_type = AU_IPv4 },
196 					.ai_asid = AU_ASSIGN_ASID,
197 					.ai_auid = getuid(),
198 					.ai_flags = 0,
199 				};
200 				if (setaudit_addr(&auinfo, sizeof(auinfo)) == 0) {
201 					char session[16];
202 					snprintf(session, sizeof(session), "%x", auinfo.ai_asid);
203 					setenv("SECURITYSESSIONID", session, 1);
204 				} else {
205 					syslog(LOG_NOTICE, "%s: Setting Audit Session ID failed: %d", prog, errno);
206 				}
207 			}
208 #endif // !TARGET_OS_EMBEDDED
209 			fcntl(r, F_SETFL, 0);
210 			fcntl(r, F_SETFD, 1);
211 			dup2(r, STDIN_FILENO);
212 			if (dupstdout)
213 				dup2(r, STDOUT_FILENO);
214 			if (dupstderr)
215 				dup2(r, STDERR_FILENO);
216 			signal(SIGCHLD, SIG_DFL);
217 			execv(prog, argv + 1);
218 			syslog(LOG_ERR, "execv(): %m");
219 			exit(EXIT_FAILURE);
220 		}
221 	}
222 
223 out:
224 	exit(ec);
225 }
226