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