Line data Source code
1 : /*
2 : SSSD
3 :
4 : Service monitor - D-BUS features
5 :
6 : Copyright (C) Stephen Gallagher 2008
7 :
8 : This program is free software; you can redistribute it and/or modify
9 : it under the terms of the GNU General Public License as published by
10 : the Free Software Foundation; either version 3 of the License, or
11 : (at your option) any later version.
12 :
13 : This program is distributed in the hope that it will be useful,
14 : but WITHOUT ANY WARRANTY; without even the implied warranty of
15 : MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
16 : GNU General Public License for more details.
17 :
18 : You should have received a copy of the GNU General Public License
19 : along with this program. If not, see <http://www.gnu.org/licenses/>.
20 : */
21 : #include <sys/time.h>
22 : #include <sys/types.h>
23 : #include <sys/stat.h>
24 : #include <tevent.h>
25 : #include <dbus/dbus.h>
26 :
27 : #include "util/util.h"
28 : #include "sbus/sssd_dbus.h"
29 : #include "sbus/sssd_dbus_private.h"
30 :
31 : static int sbus_server_destructor(void *ctx);
32 :
33 : /*
34 : * new_connection_callback
35 : * Actions to be run upon each new client connection
36 : * Must either perform dbus_connection_ref() on the
37 : * new connection or else close the connection with
38 : * dbus_connection_close()
39 : */
40 0 : static void sbus_server_init_new_connection(DBusServer *dbus_server,
41 : DBusConnection *dbus_conn,
42 : void *data)
43 : {
44 : struct sbus_connection *server;
45 : struct sbus_connection *conn;
46 : int ret;
47 :
48 0 : DEBUG(SSSDBG_FUNC_DATA,"Entering.\n");
49 0 : server = talloc_get_type(data, struct sbus_connection);
50 0 : if (!server) {
51 0 : return;
52 : }
53 :
54 0 : DEBUG(SSSDBG_FUNC_DATA,"Adding connection %p.\n", dbus_conn);
55 0 : ret = sbus_init_connection(server, server->ev, dbus_conn,
56 : SBUS_CONN_TYPE_PRIVATE, &conn);
57 0 : if (ret != 0) {
58 0 : dbus_connection_close(dbus_conn);
59 0 : DEBUG(SSSDBG_FUNC_DATA, "Closing connection (failed setup)\n");
60 0 : return;
61 : }
62 :
63 0 : dbus_connection_ref(dbus_conn);
64 :
65 0 : DEBUG(SSSDBG_FUNC_DATA,"Got a connection\n");
66 :
67 : /*
68 : * Initialize connection-specific features
69 : * This function (or its callbacks) should also
70 : * set up connection-specific methods.
71 : */
72 0 : ret = server->srv_init_fn(conn, server->srv_init_data);
73 0 : if (ret != EOK) {
74 0 : DEBUG(SSSDBG_CRIT_FAILURE,"Initialization failed!\n");
75 0 : dbus_connection_close(dbus_conn);
76 0 : talloc_zfree(conn);
77 : }
78 : }
79 :
80 : const char *
81 0 : get_socket_address(TALLOC_CTX *mem_ctx, const char *address, bool use_symlink)
82 : {
83 0 : if (!use_symlink) {
84 0 : return talloc_strdup(mem_ctx, address);
85 : }
86 :
87 0 : return talloc_asprintf(mem_ctx,
88 0 : "%s.%lu", address, (unsigned long) getpid());
89 : }
90 :
91 : static errno_t
92 0 : create_socket_symlink(const char *filename, const char *symlink_filename)
93 : {
94 : errno_t ret;
95 :
96 0 : DEBUG(SSSDBG_TRACE_LIBS, "Symlinking the dbus path %s to a link %s\n",
97 : filename, symlink_filename);
98 0 : errno = 0;
99 0 : ret = symlink(filename, symlink_filename);
100 0 : if (ret != 0 && errno == EEXIST) {
101 : /* Perhaps cruft after a previous server? */
102 0 : errno = 0;
103 0 : ret = unlink(symlink_filename);
104 0 : if (ret != 0) {
105 0 : ret = errno;
106 0 : DEBUG(SSSDBG_CRIT_FAILURE, "Cannot remove old symlink: [%d][%s].\n",
107 : ret, strerror(ret));
108 0 : return EIO;
109 : }
110 0 : errno = 0;
111 0 : ret = symlink(filename, symlink_filename);
112 : }
113 :
114 0 : if (ret != 0) {
115 0 : ret = errno;
116 0 : DEBUG(SSSDBG_CRIT_FAILURE, "symlink() failed on file '%s': [%d][%s].\n",
117 : filename, ret, strerror(ret));
118 0 : return EIO;
119 : }
120 :
121 0 : return EOK;
122 : }
123 :
124 : static errno_t
125 0 : remove_socket_symlink(const char *symlink_name)
126 : {
127 : errno_t ret;
128 : char target[PATH_MAX];
129 : char pidpath[PATH_MAX];
130 0 : ssize_t numread = 0;
131 :
132 0 : errno = 0;
133 0 : numread = readlink(symlink_name, target, PATH_MAX-1);
134 0 : if (numread < 0) {
135 0 : ret = errno;
136 0 : DEBUG(SSSDBG_OP_FAILURE,
137 : "readlink failed [%d]: %s\n", ret, strerror(ret));
138 0 : return ret;
139 : }
140 0 : target[numread] = '\0';
141 0 : DEBUG(SSSDBG_TRACE_ALL, "The symlink points to [%s]\n", target);
142 :
143 : /* We can only remove the symlink if it points to a socket with
144 : * the same PID */
145 0 : ret = snprintf(pidpath, PATH_MAX, "%s.%lu",
146 0 : symlink_name, (unsigned long) getpid());
147 0 : if (ret < 0) {
148 0 : DEBUG(SSSDBG_OP_FAILURE, "snprintf failed\n");
149 0 : return EIO;
150 0 : } else if (ret >= PATH_MAX) {
151 0 : DEBUG(SSSDBG_OP_FAILURE, "path too long?!?!\n");
152 0 : return EIO;
153 : }
154 0 : DEBUG(SSSDBG_TRACE_ALL, "The path including our pid is [%s]\n", pidpath);
155 :
156 0 : if (strcmp(pidpath, target) != 0) {
157 0 : DEBUG(SSSDBG_CONF_SETTINGS,
158 : "Will not remove symlink, seems to be owned by "
159 : "another process\n");
160 0 : return EOK;
161 : }
162 :
163 0 : ret = unlink(symlink_name);
164 0 : if (ret != 0) {
165 0 : ret = errno;
166 0 : DEBUG(SSSDBG_CRIT_FAILURE,
167 : "unlink failed to remove [%s] [%d]: %s\n",
168 : symlink_name, ret, strerror(ret));
169 0 : return ret;
170 : }
171 :
172 0 : DEBUG(SSSDBG_TRACE_ALL, "Removed the symlink\n");
173 0 : return EOK;
174 : }
175 :
176 : /*
177 : * dbus_new_server
178 : * Set up a D-BUS server, integrate with the event loop
179 : * for handling file descriptor and timed events
180 : */
181 0 : int sbus_new_server(TALLOC_CTX *mem_ctx,
182 : struct tevent_context *ev,
183 : const char *address,
184 : uid_t uid, gid_t gid,
185 : bool use_symlink,
186 : struct sbus_connection **_server,
187 : sbus_server_conn_init_fn init_fn,
188 : void *init_pvt_data)
189 : {
190 : struct sbus_connection *server;
191 : DBusServer *dbus_server;
192 : DBusError dbus_error;
193 : dbus_bool_t dbret;
194 : char *tmp;
195 : int ret, tmp_ret;
196 : char *filename;
197 0 : char *symlink_filename = NULL;
198 : const char *socket_address;
199 : struct stat stat_buf;
200 : TALLOC_CTX *tmp_ctx;
201 :
202 0 : *_server = NULL;
203 :
204 0 : tmp_ctx = talloc_new(NULL);
205 0 : if (!tmp_ctx) return ENOMEM;
206 :
207 0 : socket_address = get_socket_address(tmp_ctx, address, use_symlink);
208 0 : if (!socket_address) {
209 0 : ret = ENOMEM;
210 0 : goto done;
211 : }
212 :
213 : /* Set up D-BUS server */
214 0 : dbus_error_init(&dbus_error);
215 0 : dbus_server = dbus_server_listen(socket_address, &dbus_error);
216 0 : if (!dbus_server) {
217 0 : DEBUG(SSSDBG_CRIT_FAILURE,
218 : "dbus_server_listen failed! (name=%s, message=%s)\n",
219 : dbus_error.name, dbus_error.message);
220 0 : if (dbus_error_is_set(&dbus_error)) dbus_error_free(&dbus_error);
221 0 : ret = EIO;
222 0 : goto done;
223 : }
224 :
225 0 : filename = strchr(socket_address, '/');
226 0 : if (filename == NULL) {
227 0 : DEBUG(SSSDBG_CRIT_FAILURE,
228 : "Unexpected dbus address [%s].\n", socket_address);
229 0 : ret = EIO;
230 0 : goto done;
231 : }
232 :
233 0 : if (use_symlink) {
234 0 : symlink_filename = strchr(address, '/');
235 0 : if (symlink_filename == NULL) {
236 0 : DEBUG(SSSDBG_CRIT_FAILURE,
237 : "Unexpected dbus address [%s].\n", address);
238 0 : ret = EIO;
239 0 : goto done;
240 : }
241 :
242 0 : ret = create_socket_symlink(filename, symlink_filename);
243 0 : if (ret != EOK) {
244 0 : DEBUG(SSSDBG_CRIT_FAILURE, "Could not create symlink [%d]: %s\n",
245 : ret, strerror(ret));
246 0 : ret = EIO;
247 0 : goto done;
248 : }
249 : }
250 :
251 : /* Both check_file and chmod can handle both the symlink and
252 : * the socket */
253 0 : ret = check_file(filename,
254 : getuid(), getgid(), S_IFSOCK, S_IFMT, &stat_buf, true);
255 0 : if (ret != EOK) {
256 0 : DEBUG(SSSDBG_CRIT_FAILURE, "check_file failed for [%s].\n", filename);
257 0 : ret = EIO;
258 0 : goto done;
259 : }
260 :
261 0 : if ((stat_buf.st_mode & ~S_IFMT) != (S_IRUSR|S_IWUSR)) {
262 0 : ret = chmod(filename, (S_IRUSR|S_IWUSR));
263 0 : if (ret != EOK) {
264 0 : ret = errno;
265 0 : DEBUG(SSSDBG_CRIT_FAILURE,
266 : "chmod failed for [%s]: [%d][%s].\n", filename, ret,
267 : sss_strerror(ret));
268 0 : ret = EIO;
269 0 : goto done;
270 : }
271 : }
272 :
273 0 : if (stat_buf.st_uid != uid || stat_buf.st_gid != gid) {
274 0 : ret = chown(filename, uid, gid);
275 0 : if (ret != EOK) {
276 0 : ret = errno;
277 0 : DEBUG(SSSDBG_CRIT_FAILURE,
278 : "chown failed for [%s]: [%d][%s].\n", filename, ret,
279 : sss_strerror(ret));
280 0 : ret = EIO;
281 0 : goto done;
282 : }
283 : }
284 :
285 0 : tmp = dbus_server_get_address(dbus_server);
286 0 : DEBUG(SSSDBG_TRACE_FUNC, "D-BUS Server listening on %s\n", tmp);
287 0 : free(tmp);
288 :
289 0 : server = talloc_zero(tmp_ctx, struct sbus_connection);
290 0 : if (!server) {
291 0 : ret = ENOMEM;
292 0 : goto done;
293 : }
294 :
295 0 : server->ev = ev;
296 0 : server->type = SBUS_SERVER;
297 0 : server->dbus.server = dbus_server;
298 0 : server->srv_init_fn = init_fn;
299 0 : server->srv_init_data = init_pvt_data;
300 :
301 0 : talloc_set_destructor((TALLOC_CTX *)server, sbus_server_destructor);
302 :
303 0 : if (use_symlink) {
304 0 : server->symlink = talloc_strdup(server, symlink_filename);
305 0 : if (!server->symlink) {
306 0 : ret = ENOMEM;
307 0 : goto done;
308 : }
309 : }
310 :
311 : /* Set up D-BUS new connection handler */
312 0 : dbus_server_set_new_connection_function(server->dbus.server,
313 : sbus_server_init_new_connection,
314 : server, NULL);
315 :
316 : /* Set up DBusWatch functions */
317 0 : dbret = dbus_server_set_watch_functions(server->dbus.server,
318 : sbus_add_watch,
319 : sbus_remove_watch,
320 : sbus_toggle_watch,
321 : server, NULL);
322 0 : if (!dbret) {
323 0 : DEBUG(SSSDBG_CONF_SETTINGS,
324 : "Error setting up D-BUS server watch functions\n");
325 0 : ret = EIO;
326 0 : goto done;
327 : }
328 :
329 : /* Set up DBusTimeout functions */
330 0 : dbret = dbus_server_set_timeout_functions(server->dbus.server,
331 : sbus_add_timeout,
332 : sbus_remove_timeout,
333 : sbus_toggle_timeout,
334 : server, NULL);
335 0 : if (!dbret) {
336 0 : DEBUG(SSSDBG_CONF_SETTINGS,
337 : "Error setting up D-BUS server timeout functions\n");
338 0 : dbus_server_set_watch_functions(server->dbus.server,
339 : NULL, NULL, NULL, NULL, NULL);
340 0 : ret = EIO;
341 0 : goto done;
342 : }
343 :
344 0 : *_server = talloc_steal(mem_ctx, server);
345 0 : ret = EOK;
346 :
347 : done:
348 0 : if (ret != EOK && symlink_filename) {
349 0 : tmp_ret = unlink(symlink_filename);
350 : /* non-fatal failure */
351 0 : if (tmp_ret != EOK) {
352 0 : tmp_ret = errno;
353 0 : DEBUG(SSSDBG_MINOR_FAILURE,
354 : "Failed to remove symbolic link: %d [%s]!\n",
355 : tmp_ret, sss_strerror(tmp_ret));
356 : }
357 : }
358 0 : talloc_free(tmp_ctx);
359 0 : return ret;
360 : }
361 :
362 0 : static int sbus_server_destructor(void *ctx)
363 : {
364 : struct sbus_connection *server;
365 : errno_t ret;
366 :
367 0 : server = talloc_get_type(ctx, struct sbus_connection);
368 0 : dbus_server_disconnect(server->dbus.server);
369 :
370 0 : if (server->symlink) {
371 0 : ret = remove_socket_symlink(server->symlink);
372 0 : if (ret != EOK) {
373 0 : DEBUG(SSSDBG_MINOR_FAILURE,
374 : "Could not remove the server symlink\n");
375 : }
376 : }
377 :
378 0 : return 0;
379 : }
|