1 /* -*- Mode: C; tab-width: 8; indent-tabs-mode: nil; c-basic-offset: 8 -*-
2 *
3 * Copyright (C) 2005 Raffaele Sandrini
4 * Copyright (C) 2005 Red Hat, Inc.
5 * Copyright (C) 2002, 2003 George Lebl
6 * Copyright (C) 2001 Queen of England,
7 * Copyright (C) 2012-2021 MATE Developers
8 *
9 * This program is free software; you can redistribute it and/or
10 * modify it under the terms of the GNU General Public License as
11 * published by the Free Software Foundation; either version 2 of the
12 * License, or (at your option) any later version.
13 *
14 * This program is distributed in the hope that it will be useful, but
15 * WITHOUT ANY WARRANTY; without even the implied warranty of
16 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
17 * General Public License for more details.
18 *
19 * You should have received a copy of the GNU General Public License
20 * along with this program; if not, write to the Free Software
21 * Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA
22 * 02110-1301, USA.
23 *
24 * Authors:
25 * Raffaele Sandrini <rasa@gmx.ch>
26 * George Lebl <jirka@5z.com>
27 * Mark McLoughlin <mark@skynet.ie>
28 */
29
30 #ifdef HAVE_CONFIG_H
31 #include <config.h>
32 #endif
33
34 #include <string.h>
35 #include <errno.h>
36 #include <unistd.h>
37 #include <time.h>
38 #include <sys/socket.h>
39 #include <sys/un.h>
40
41 #include <X11/Xauth.h>
42 #include <gdk/gdk.h>
43
44 #include "mdm.h"
45
46 #define MDM_PROTOCOL_UPDATE_INTERVAL 1 /* seconds */
47
48 #define MDM_PROTOCOL_SOCKET_PATH "/var/run/mdm_socket"
49
50 #define MDM_PROTOCOL_MSG_CLOSE "CLOSE"
51 #define MDM_PROTOCOL_MSG_VERSION "VERSION"
52 #define MDM_PROTOCOL_MSG_AUTHENTICATE "AUTH_LOCAL"
53 #define MDM_PROTOCOL_MSG_QUERY_ACTION "QUERY_LOGOUT_ACTION"
54 #define MDM_PROTOCOL_MSG_SET_ACTION "SET_SAFE_LOGOUT_ACTION"
55 #define MDM_PROTOCOL_MSG_FLEXI_XSERVER "FLEXI_XSERVER"
56
57 #define MDM_ACTION_STR_NONE "NONE"
58 #define MDM_ACTION_STR_SHUTDOWN "HALT"
59 #define MDM_ACTION_STR_REBOOT "REBOOT"
60 #define MDM_ACTION_STR_SUSPEND "SUSPEND"
61
62 typedef struct {
63 int fd;
64 char* auth_cookie;
65
66 MdmLogoutAction available_actions;
67 MdmLogoutAction current_actions;
68
69 time_t last_update;
70 } MdmProtocolData;
71
72 static MdmProtocolData mdm_protocol_data = {
73 0,
74 NULL,
75 MDM_LOGOUT_ACTION_NONE,
76 MDM_LOGOUT_ACTION_NONE,
77 0
78 };
79
mdm_send_protocol_msg(MdmProtocolData * data,const char * msg)80 static char* mdm_send_protocol_msg(MdmProtocolData* data, const char* msg)
81 {
82 GString* retval;
83 char buf[256];
84 char* p;
85 int len;
86
87 p = g_strconcat(msg, "\n", NULL);
88
89 if (write(data->fd, p, strlen(p)) < 0)
90 {
91 g_free(p);
92
93 g_warning("Failed to send message to MDM: %s", g_strerror(errno));
94
95 return NULL;
96 }
97
98 g_free(p);
99
100 p = NULL;
101 retval = NULL;
102
103 while ((len = read(data->fd, buf, sizeof(buf) - 1)) > 0)
104 {
105 buf[len] = '\0';
106
107 if (!retval)
108 {
109 retval = g_string_new(buf);
110 }
111 else
112 {
113 retval = g_string_append(retval, buf);
114 }
115
116 if ((p = strchr(retval->str, '\n')))
117 {
118 break;
119 }
120 }
121
122 if (p)
123 {
124 *p = '\0';
125 }
126
127 return retval ? g_string_free(retval, FALSE) : NULL;
128 }
129
get_display_number(void)130 static char* get_display_number(void)
131 {
132 const char* display_name;
133 char* retval;
134 char* p;
135
136 display_name = gdk_display_get_name(gdk_display_get_default());
137
138 p = strchr(display_name, ':');
139
140 if (!p)
141 {
142 return g_strdup("0");
143 }
144
145 while (*p == ':')
146 {
147 p++;
148 }
149
150 retval = g_strdup(p);
151
152 p = strchr(retval, '.');
153
154 if (p != NULL)
155 {
156 *p = '\0';
157 }
158
159 return retval;
160 }
161
mdm_authenticate_connection(MdmProtocolData * data)162 static gboolean mdm_authenticate_connection(MdmProtocolData* data)
163 {
164 #define MDM_MIT_MAGIC_COOKIE_LEN 16
165
166 const char* xau_path;
167 FILE* f;
168 Xauth* xau;
169 char* display_number;
170 gboolean retval;
171
172 if (data->auth_cookie)
173 {
174 char* msg;
175 char* response;
176
177 msg = g_strdup_printf(MDM_PROTOCOL_MSG_AUTHENTICATE " %s", data->auth_cookie);
178 response = mdm_send_protocol_msg(data, msg);
179 g_free(msg);
180
181 if (response && !strcmp(response, "OK"))
182 {
183 g_free(response);
184 return TRUE;
185 }
186 else
187 {
188 g_free(response);
189 g_free(data->auth_cookie);
190 data->auth_cookie = NULL;
191 }
192 }
193
194 if (!(xau_path = XauFileName()))
195 {
196 return FALSE;
197 }
198
199 if (!(f = fopen(xau_path, "r")))
200 {
201 return FALSE;
202 }
203
204 retval = FALSE;
205 display_number = get_display_number();
206
207 while ((xau = XauReadAuth(f)))
208 {
209 char buffer[40]; /* 2*16 == 32, so 40 is enough */
210 char* msg;
211 char* response;
212 int i;
213
214 if (xau->family != FamilyLocal || strncmp(xau->number, display_number, xau->number_length) || strncmp(xau->name, "MIT-MAGIC-COOKIE-1", xau->name_length) || xau->data_length != MDM_MIT_MAGIC_COOKIE_LEN)
215 {
216 XauDisposeAuth(xau);
217 continue;
218 }
219
220 for (i = 0; i < MDM_MIT_MAGIC_COOKIE_LEN; i++)
221 {
222 g_snprintf(buffer + 2 * i, 3, "%02x", (guint)(guchar) xau->data[i]);
223 }
224
225 XauDisposeAuth(xau);
226
227 msg = g_strdup_printf(MDM_PROTOCOL_MSG_AUTHENTICATE " %s", buffer);
228 response = mdm_send_protocol_msg(data, msg);
229 g_free(msg);
230
231 if (response && !strcmp(response, "OK"))
232 {
233 data->auth_cookie = g_strdup(buffer);
234 g_free(response);
235 retval = TRUE;
236 break;
237 }
238
239 g_free(response);
240 }
241
242 g_free(display_number);
243
244 fclose(f);
245
246 return retval;
247
248 #undef MDM_MIT_MAGIC_COOKIE_LEN
249 }
250
mdm_shutdown_protocol_connection(MdmProtocolData * data)251 static void mdm_shutdown_protocol_connection(MdmProtocolData *data)
252 {
253 if (data->fd)
254 {
255 close(data->fd);
256 }
257
258 data->fd = 0;
259 }
260
mdm_init_protocol_connection(MdmProtocolData * data)261 static gboolean mdm_init_protocol_connection(MdmProtocolData* data)
262 {
263 struct sockaddr_un addr;
264 char* response;
265
266 g_assert(data->fd <= 0);
267
268 if (g_file_test(MDM_PROTOCOL_SOCKET_PATH, G_FILE_TEST_EXISTS))
269 {
270 g_strlcpy (addr.sun_path, MDM_PROTOCOL_SOCKET_PATH, sizeof (addr.sun_path));
271 }
272 else if (g_file_test("/tmp/.mdm_socket", G_FILE_TEST_EXISTS))
273 {
274 g_strlcpy (addr.sun_path, "/tmp/.mdm_socket", sizeof (addr.sun_path));
275 }
276 else
277 {
278 return FALSE;
279 }
280
281 data->fd = socket(AF_UNIX, SOCK_STREAM, 0);
282
283 if (data->fd < 0)
284 {
285 g_warning("Failed to create MDM socket: %s", g_strerror(errno));
286
287 mdm_shutdown_protocol_connection(data);
288
289 return FALSE;
290 }
291
292 addr.sun_family = AF_UNIX;
293
294 if (connect(data->fd, (struct sockaddr*) &addr, sizeof(addr)) < 0)
295 {
296 g_warning("Failed to establish a connection with MDM: %s", g_strerror(errno));
297
298 mdm_shutdown_protocol_connection(data);
299
300 return FALSE;
301 }
302
303 response = mdm_send_protocol_msg(data, MDM_PROTOCOL_MSG_VERSION);
304
305 if (!response || strncmp(response, "MDM ", strlen("MDM ")) != 0)
306 {
307 g_free(response);
308
309 g_warning("Failed to get protocol version from MDM");
310 mdm_shutdown_protocol_connection(data);
311
312 return FALSE;
313 }
314
315 g_free(response);
316
317 if (!mdm_authenticate_connection(data))
318 {
319 g_warning("Failed to authenticate with MDM");
320 mdm_shutdown_protocol_connection(data);
321 return FALSE;
322 }
323
324 return TRUE;
325 }
326
mdm_parse_query_response(MdmProtocolData * data,const char * response)327 static void mdm_parse_query_response(MdmProtocolData* data, const char* response)
328 {
329 char** actions;
330 int i;
331
332 data->available_actions = MDM_LOGOUT_ACTION_NONE;
333 data->current_actions = MDM_LOGOUT_ACTION_NONE;
334
335 if (strncmp(response, "OK ", 3) != 0)
336 {
337 return;
338 }
339
340 response += 3;
341
342 actions = g_strsplit(response, ";", -1);
343
344 for (i = 0; actions[i]; i++)
345 {
346 MdmLogoutAction action = MDM_LOGOUT_ACTION_NONE;
347 gboolean selected = FALSE;
348 char* str = actions [i];
349 int len;
350
351 len = strlen(str);
352
353 if (!len)
354 {
355 continue;
356 }
357
358 if (str[len - 1] == '!')
359 {
360 selected = TRUE;
361 str[len - 1] = '\0';
362 }
363
364 if (!strcmp(str, MDM_ACTION_STR_SHUTDOWN))
365 {
366 action = MDM_LOGOUT_ACTION_SHUTDOWN;
367 }
368 else if (!strcmp(str, MDM_ACTION_STR_REBOOT))
369 {
370 action = MDM_LOGOUT_ACTION_REBOOT;
371 }
372 else if (!strcmp(str, MDM_ACTION_STR_SUSPEND))
373 {
374 action = MDM_LOGOUT_ACTION_SUSPEND;
375 }
376
377 data->available_actions |= action;
378
379 if (selected)
380 {
381 data->current_actions |= action;
382 }
383 }
384
385 g_strfreev(actions);
386 }
387
mdm_update_logout_actions(MdmProtocolData * data)388 static void mdm_update_logout_actions(MdmProtocolData* data)
389 {
390 time_t current_time;
391 char* response;
392
393 current_time = time(NULL);
394
395 if (current_time <= (data->last_update + MDM_PROTOCOL_UPDATE_INTERVAL))
396 {
397 return;
398 }
399
400 data->last_update = current_time;
401
402 if (!mdm_init_protocol_connection(data))
403 {
404 return;
405 }
406
407 if ((response = mdm_send_protocol_msg(data, MDM_PROTOCOL_MSG_QUERY_ACTION)))
408 {
409 mdm_parse_query_response(data, response);
410 g_free(response);
411 }
412
413 mdm_shutdown_protocol_connection(data);
414 }
415
mdm_is_available(void)416 gboolean mdm_is_available(void)
417 {
418 if (!mdm_init_protocol_connection(&mdm_protocol_data))
419 {
420 return FALSE;
421 }
422
423 mdm_shutdown_protocol_connection(&mdm_protocol_data);
424
425 return TRUE;
426 }
427
mdm_supports_logout_action(MdmLogoutAction action)428 gboolean mdm_supports_logout_action(MdmLogoutAction action)
429 {
430 mdm_update_logout_actions(&mdm_protocol_data);
431
432 return (mdm_protocol_data.available_actions & action) != 0;
433 }
434
mdm_get_logout_action(void)435 MdmLogoutAction mdm_get_logout_action(void)
436 {
437 mdm_update_logout_actions(&mdm_protocol_data);
438
439 return mdm_protocol_data.current_actions;
440 }
441
mdm_set_logout_action(MdmLogoutAction action)442 void mdm_set_logout_action(MdmLogoutAction action)
443 {
444 char* action_str = NULL;
445 char* msg;
446 char* response;
447
448 if (!mdm_init_protocol_connection(&mdm_protocol_data))
449 {
450 return;
451 }
452
453 switch (action)
454 {
455 case MDM_LOGOUT_ACTION_NONE:
456 action_str = MDM_ACTION_STR_NONE;
457 break;
458 case MDM_LOGOUT_ACTION_SHUTDOWN:
459 action_str = MDM_ACTION_STR_SHUTDOWN;
460 break;
461 case MDM_LOGOUT_ACTION_REBOOT:
462 action_str = MDM_ACTION_STR_REBOOT;
463 break;
464 case MDM_LOGOUT_ACTION_SUSPEND:
465 action_str = MDM_ACTION_STR_SUSPEND;
466 break;
467 }
468
469 msg = g_strdup_printf(MDM_PROTOCOL_MSG_SET_ACTION " %s", action_str);
470
471 response = mdm_send_protocol_msg(&mdm_protocol_data, msg);
472
473 g_free(msg);
474 g_free(response);
475
476 mdm_protocol_data.last_update = 0;
477
478 mdm_shutdown_protocol_connection(&mdm_protocol_data);
479 }
480
mdm_new_login(void)481 void mdm_new_login(void)
482 {
483 char* response;
484
485 if (!mdm_init_protocol_connection(&mdm_protocol_data))
486 {
487 return;
488 }
489
490 response = mdm_send_protocol_msg(&mdm_protocol_data, MDM_PROTOCOL_MSG_FLEXI_XSERVER);
491
492 g_free(response);
493
494 mdm_protocol_data.last_update = 0;
495
496 mdm_shutdown_protocol_connection(&mdm_protocol_data);
497 }
498