1 /* |
|
2 * CDDL HEADER START |
|
3 * |
|
4 * The contents of this file are subject to the terms of the |
|
5 * Common Development and Distribution License (the "License"). |
|
6 * You may not use this file except in compliance with the License. |
|
7 * |
|
8 * You can obtain a copy of the license at usr/src/OPENSOLARIS.LICENSE |
|
9 * or http://www.opensolaris.org/os/licensing. |
|
10 * See the License for the specific language governing permissions |
|
11 * and limitations under the License. |
|
12 * |
|
13 * When distributing Covered Code, include this CDDL HEADER in each |
|
14 * file and include the License file at usr/src/OPENSOLARIS.LICENSE. |
|
15 * If applicable, add the following below this CDDL HEADER, with the |
|
16 * fields enclosed by brackets "[]" replaced with your own identifying |
|
17 * information: Portions Copyright [yyyy] [name of copyright owner] |
|
18 * |
|
19 * CDDL HEADER END |
|
20 */ |
|
21 |
|
22 /* |
|
23 * Copyright (c) 2010, 2012, Oracle and/or its affiliates. All rights reserved. |
|
24 */ |
|
25 |
|
26 #include <sys/types.h> |
|
27 #include <sys/socket.h> |
|
28 #include <sys/stat.h> |
|
29 #include <bsm/adt_event.h> |
|
30 #include <stdio.h> |
|
31 #include <string.h> |
|
32 #include <stdlib.h> |
|
33 #include <errno.h> |
|
34 #include <unistd.h> |
|
35 #include <pthread.h> |
|
36 #include <ucred.h> |
|
37 #include <zone.h> |
|
38 |
|
39 #include <rad/adr_stream.h> |
|
40 #include "rad_object.h" |
|
41 #include "rad_modapi.h" |
|
42 #include "rad_modapi_xport.h" |
|
43 #include "rad_connection.h" |
|
44 #include "rad_xport.h" |
|
45 |
|
46 #include "api_unix.h" |
|
47 |
|
48 static char *pam_service = "rad-unix"; |
|
49 |
|
50 static boolean_t |
|
51 sockaddr_init(struct sockaddr_un *addr, const char *name) |
|
52 { |
|
53 size_t namelen; |
|
54 size_t addrlen; |
|
55 |
|
56 (void) memset(addr, 0, sizeof (*addr)); |
|
57 addr->sun_family = AF_UNIX; |
|
58 |
|
59 namelen = strlen(name); |
|
60 addrlen = sizeof (addr->sun_path); |
|
61 |
|
62 if (namelen >= addrlen) |
|
63 return (B_FALSE); |
|
64 |
|
65 (void) strlcpy(addr->sun_path, name, sizeof (addr->sun_path)); |
|
66 return (B_TRUE); |
|
67 } |
|
68 |
|
69 static int |
|
70 create_tmpdir(const char *name) |
|
71 { |
|
72 int retval = 0; |
|
73 struct stat st; |
|
74 mode_t um; |
|
75 |
|
76 int i = strncmp(name, RAD_TMPDIR "/", strlen(RAD_TMPDIR "/")); |
|
77 |
|
78 if (i == 0) { /* Default path specified */ |
|
79 if (stat(RAD_TMPDIR, &st) == 0) { |
|
80 if (!S_ISDIR(st.st_mode)) { |
|
81 rad_log(RL_ERROR, "file '%s' exists.", |
|
82 RAD_TMPDIR); |
|
83 retval = -1; |
|
84 } |
|
85 } else if (errno == ENOENT) { /* Create it */ |
|
86 um = umask(0); |
|
87 i = mkdir(RAD_TMPDIR, S_IRWXU | S_IRWXG | S_IRWXO); |
|
88 (void) umask(um); |
|
89 if (i != 0) { |
|
90 rad_log(RL_ERROR, "error creating '%s': %s." |
|
91 RAD_TMPDIR, strerror(errno)); |
|
92 retval = -1; |
|
93 } |
|
94 } else { |
|
95 rad_log(RL_ERROR, "error creating '%s': %s." |
|
96 RAD_TMPDIR, strerror(errno)); |
|
97 retval = -1; |
|
98 } |
|
99 } |
|
100 return (retval); |
|
101 } |
|
102 |
|
103 static int |
|
104 listen_on_name(const char *name) |
|
105 { |
|
106 int fd; |
|
107 struct sockaddr_un addr; |
|
108 |
|
109 if (create_tmpdir(name) != 0) |
|
110 return (-1); |
|
111 |
|
112 if (unlink(name) == -1 && errno != ENOENT) { |
|
113 rad_log(RL_ERROR, "unlink of '%s' failed: %s", name, |
|
114 strerror(errno)); |
|
115 return (-1); |
|
116 } |
|
117 |
|
118 if (!sockaddr_init(&addr, name)) { |
|
119 rad_log(RL_ERROR, "socket name '%s' too long", name); |
|
120 return (-1); |
|
121 } |
|
122 |
|
123 if ((fd = socket(AF_UNIX, SOCK_STREAM, 0)) == -1) { |
|
124 rad_log(RL_ERROR, "socket failed: %s", strerror(errno)); |
|
125 return (-1); |
|
126 } |
|
127 |
|
128 if (bind(fd, (struct sockaddr *)&addr, sizeof (addr)) == -1) { |
|
129 rad_log(RL_ERROR, "bind to '%s' failed: %s", name, |
|
130 strerror(errno)); |
|
131 (void) close(fd); |
|
132 return (-1); |
|
133 } |
|
134 |
|
135 if (listen(fd, 15) == -1) { |
|
136 rad_log(RL_ERROR, "listen on '%s' failed: %s", name, |
|
137 strerror(errno)); |
|
138 return (-1); |
|
139 } |
|
140 |
|
141 return (fd); |
|
142 } |
|
143 |
|
144 /* |
|
145 * Determines if the ucred represents someone who is effectively us. |
|
146 */ |
|
147 static boolean_t |
|
148 sent_by_joe(ucred_t *uc) |
|
149 { |
|
150 const priv_set_t *theirprivs; |
|
151 priv_set_t *myprivs = priv_allocset(); |
|
152 if (myprivs == NULL) { |
|
153 rad_log(RL_ERROR, "failed to allocate privilege set"); |
|
154 return (B_FALSE); |
|
155 } |
|
156 |
|
157 /* Could handle this "gracefully", but it isn't supposed to fail */ |
|
158 if (getppriv(PRIV_PERMITTED, myprivs) == -1) |
|
159 rad_log(RL_FATAL, "getppriv(PRIV_PERMITTED) failed: %s", |
|
160 strerror(errno)); |
|
161 |
|
162 if (uc == NULL || |
|
163 ucred_geteuid(uc) != getuid() || |
|
164 ucred_getzoneid(uc) != getzoneid() || |
|
165 (theirprivs = ucred_getprivset(uc, PRIV_EFFECTIVE)) == NULL || |
|
166 !priv_issubset(myprivs, theirprivs)) { |
|
167 priv_freeset(myprivs); |
|
168 rad_log(RL_WARN, "unprivileged client (uid=%d) " |
|
169 "attempted connection to control port", ucred_geteuid(uc)); |
|
170 return (B_FALSE); |
|
171 } |
|
172 |
|
173 priv_freeset(myprivs); |
|
174 return (B_TRUE); |
|
175 } |
|
176 |
|
177 static void |
|
178 unix_run(void *arg) |
|
179 { |
|
180 radmod_connection_t *conn = arg; |
|
181 rad_proto_handle(conn); |
|
182 rad_conn_free(conn); |
|
183 } |
|
184 |
|
185 static rad_moderr_t |
|
186 unix_listen(rad_thread_t *arg) |
|
187 { |
|
188 data_t *data = rad_thread_arg(arg); |
|
189 int fd; |
|
190 data_t *d, *path = struct_get(data, "path"); |
|
191 d = struct_get(data, "proto"); |
|
192 const char *protostr = d != NULL ? data_to_string(d) : "rad"; |
|
193 d = struct_get(data, "control"); |
|
194 boolean_t control = d != NULL ? data_to_boolean(d) : B_FALSE; |
|
195 d = struct_get(data, "peercred"); |
|
196 boolean_t peercred = d != NULL ? data_to_boolean(d) : B_TRUE; |
|
197 d = struct_get(data, "pam_service"); |
|
198 if (d != NULL) { |
|
199 pam_service = (char *)data_to_string(d); |
|
200 } |
|
201 |
|
202 rad_protocol_t *proto = rad_proto_find(protostr); |
|
203 if (proto == NULL) { |
|
204 rad_log(RL_ERROR, "Unable to find protocol \"%s\".", protostr); |
|
205 return (rm_config); |
|
206 } |
|
207 |
|
208 if ((fd = listen_on_name(data_to_string(path))) < 0) { |
|
209 rad_log(RL_ERROR, "Error starting AF_UNIX server: %s", |
|
210 strerror(errno)); |
|
211 return (rm_system); |
|
212 } |
|
213 |
|
214 rad_thread_ack(arg, rm_ok); |
|
215 for (;;) { |
|
216 int afd; |
|
217 |
|
218 rad_log(RL_DEBUG, "Waiting for connection.\n"); |
|
219 if ((afd = accept(fd, 0, 0)) == -1) { |
|
220 rad_log(RL_WARN, "Error in accept(): %s\n", |
|
221 strerror(errno)); |
|
222 continue; |
|
223 } |
|
224 rad_log(RL_DEBUG, "Connection accepted.\n"); |
|
225 |
|
226 /* subject allocation failure and missing ucred are conflated */ |
|
227 rad_subject_t *subject = peercred ? |
|
228 rad_subject_create_fd(afd, pam_service) : NULL; |
|
229 |
|
230 if (control) { |
|
231 if (subject == NULL) { |
|
232 (void) close(afd); |
|
233 continue; |
|
234 } |
|
235 |
|
236 if (!sent_by_joe(subject->rs_ucred)) { |
|
237 rad_subject_unref(subject); |
|
238 (void) close(afd); |
|
239 continue; |
|
240 } |
|
241 rad_log(RL_DEBUG, |
|
242 "accepting connection on control port"); |
|
243 subject->rs_control = B_TRUE; |
|
244 } |
|
245 |
|
246 adr_stream_t *stream = adr_stream_create_fd(afd); |
|
247 if (stream == NULL) |
|
248 continue; |
|
249 |
|
250 radmod_connection_t *conn = rad_conn_create_fd(afd, B_TRUE); |
|
251 if (conn == NULL) { |
|
252 adr_stream_close(stream); |
|
253 adr_stream_free(stream); |
|
254 rad_log(RL_WARN, "failed to allocate connection"); |
|
255 continue; |
|
256 } |
|
257 conn->rm_conn_xport = stream; |
|
258 conn->rm_conn_proto_ops = proto; |
|
259 conn->rm_conn_pam_service = pam_service; |
|
260 |
|
261 if (subject != NULL && |
|
262 !rad_conn_setsubject(conn, subject)) { |
|
263 rad_conn_close(conn); |
|
264 rad_conn_free(conn); |
|
265 rad_log(RL_WARN, "failed to set connection subject"); |
|
266 continue; |
|
267 } |
|
268 |
|
269 if (rad_thread_create_async(unix_run, conn) != rm_ok) { |
|
270 rad_conn_close(conn); |
|
271 rad_conn_free(conn); |
|
272 } |
|
273 } |
|
274 } |
|
275 |
|
276 static rad_moderr_t |
|
277 starter(data_t *data) |
|
278 { |
|
279 data_t *path = struct_get(data, "path"); |
|
280 |
|
281 if (path == NULL) { |
|
282 rad_log(RL_ERROR, "Unix domain socket requires path\n"); |
|
283 return (rm_config); |
|
284 } |
|
285 |
|
286 return (rad_thread_create(unix_listen, data)); |
|
287 } |
|
288 |
|
289 static rad_modinfo_t modinfo = { |
|
290 "xport_unix", "unix domain socket transport module", |
|
291 }; |
|
292 |
|
293 int |
|
294 _rad_init(void *handle) |
|
295 { |
|
296 if (rad_module_register(handle, RAD_MODVERSION, &modinfo) == -1) |
|
297 return (-1); |
|
298 |
|
299 rad_xport_register("unix", &t__unix, starter); |
|
300 return (0); |
|
301 } |
|