1 /*
2 * Copyright (C) 2009 Red Hat, Inc.
3 *
4 * This library is free software; you can redistribute it and/or
5 * modify it under the terms of the GNU Lesser General Public
6 * License as published by the Free Software Foundation; either
7 * version 2 of the License, or (at your option) any later version.
8 *
9 * This library is distributed in the hope that it will be useful,
10 * but WITHOUT ANY WARRANTY; without even the implied warranty of
11 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
12 * Lesser General Public License for more details.
13 *
14 * You should have received a copy of the GNU Lesser General
15 * Public License along with this library; if not, write to the
16 * Free Software Foundation, Inc., 59 Temple Place, Suite 330,
17 * Boston, MA 02111-1307, USA.
18 *
19 * Author: David Zeuthen <davidz@redhat.com>
20 */
21
22 #ifdef HAVE_CONFIG_H
23 # include "config.h"
24 #endif
25
26 #include <stdio.h>
27 #include <stdlib.h>
28 #include <glib/gi18n.h>
29 #include <polkit/polkit.h>
30 #define POLKIT_AGENT_I_KNOW_API_IS_SUBJECT_TO_CHANGE
31 #include <polkitagent/polkitagent.h>
32
33 static void
help(void)34 help (void)
35 {
36 g_print (_("Usage:\n"
37 " pkcheck [OPTION...]\n"
38 "\n"
39 "Help Options:\n"
40 " -h, --help Show help options\n"
41 "\n"
42 "Application Options:\n"
43 " -a, --action-id=ACTION Check authorization to perform ACTION\n"
44 " -u, --allow-user-interaction Interact with the user if necessary\n"
45 " -d, --details=KEY VALUE Add (KEY, VALUE) to information about the action\n"
46 " --enable-internal-agent Use an internal authentication agent if necessary\n"
47 " --list-temp List temporary authorizations for current session\n"
48 " -p, --process=PID[,START_TIME,UID] Check authorization of specified process\n"
49 " --revoke-temp Revoke all temporary authorizations for current session\n"
50 " -s, --system-bus-name=BUS_NAME Check authorization of owner of BUS_NAME\n"
51 " --version Show version\n"
52 "\n"
53 "Report bugs to: %s\n"
54 "%s home page: <%s>\n"), PACKAGE_BUGREPORT, PACKAGE_NAME,
55 PACKAGE_URL);
56 }
57
58 static gchar *
escape_str(const gchar * str)59 escape_str (const gchar *str)
60 {
61 GString *s;
62 guint n;
63
64 s = g_string_new (NULL);
65 if (str == NULL)
66 goto out;
67
68 for (n = 0; str[n] != '\0'; n++)
69 {
70 guint c = str[n] & 0xff;
71
72 if (g_ascii_isalnum (c) || c=='_')
73 g_string_append_c (s, c);
74 else
75 g_string_append_printf (s, "\\%o", c);
76 }
77
78 out:
79 return g_string_free (s, FALSE);
80 }
81
82 static gchar *
format_reltime(gint seconds)83 format_reltime (gint seconds)
84 {
85 gint magnitude;
86 const gchar *ending;
87 gchar *ret;
88
89 if (seconds >= 0)
90 {
91 magnitude = seconds;
92 ending = "from now";
93 }
94 else
95 {
96 magnitude = -seconds;
97 ending = "ago";
98 }
99
100 if (magnitude >= 60)
101 {
102 ret = g_strdup_printf ("%d min %d sec %s", magnitude/60, magnitude%60, ending);
103 }
104 else
105 {
106 ret = g_strdup_printf ("%d sec %s", magnitude, ending);
107 }
108
109 return ret;
110 }
111
112 /* TODO: should probably move to PolkitSubject
113 * (also see copy in src/polkitbackend/polkitbackendinteractiveauthority.c)
114 *
115 * Also, can't really trust the cmdline... but might be useful in the logs anyway.
116 */
117 static gchar *
_polkit_subject_get_cmdline(PolkitSubject * subject)118 _polkit_subject_get_cmdline (PolkitSubject *subject)
119 {
120 PolkitSubject *process;
121 gchar *ret;
122 gint pid;
123 gchar *filename;
124 gchar *contents;
125 gsize contents_len;
126 GError *error;
127 guint n;
128
129 g_return_val_if_fail (subject != NULL, NULL);
130
131 error = NULL;
132
133 ret = NULL;
134 process = NULL;
135 filename = NULL;
136 contents = NULL;
137
138 if (POLKIT_IS_UNIX_PROCESS (subject))
139 {
140 process = g_object_ref (subject);
141 }
142 else if (POLKIT_IS_SYSTEM_BUS_NAME (subject))
143 {
144 process = polkit_system_bus_name_get_process_sync (POLKIT_SYSTEM_BUS_NAME (subject),
145 NULL,
146 &error);
147 if (process == NULL)
148 {
149 g_printerr ("Error getting process for system bus name `%s': %s\n",
150 polkit_system_bus_name_get_name (POLKIT_SYSTEM_BUS_NAME (subject)),
151 error->message);
152 g_error_free (error);
153 goto out;
154 }
155 }
156 else
157 {
158 g_warning ("Unknown subject type passed to _polkit_subject_get_cmdline()");
159 goto out;
160 }
161
162 pid = polkit_unix_process_get_pid (POLKIT_UNIX_PROCESS (process));
163
164 filename = g_strdup_printf ("/proc/%d/cmdline", pid);
165
166 if (!g_file_get_contents (filename,
167 &contents,
168 &contents_len,
169 &error))
170 {
171 g_printerr ("Error opening `%s': %s\n",
172 filename,
173 error->message);
174 g_error_free (error);
175 goto out;
176 }
177
178 if (contents == NULL || contents_len == 0)
179 {
180 goto out;
181 }
182 else
183 {
184 /* The kernel uses '\0' to separate arguments - replace those with a space. */
185 for (n = 0; n < contents_len - 1; n++)
186 {
187 if (contents[n] == '\0')
188 contents[n] = ' ';
189 }
190 ret = g_strdup (contents);
191 g_strstrip (ret);
192 }
193
194 out:
195 g_free (filename);
196 g_free (contents);
197 if (process != NULL)
198 g_object_unref (process);
199 return ret;
200 }
201
202 static gint
do_list_or_revoke_temp_authz(gboolean revoke)203 do_list_or_revoke_temp_authz (gboolean revoke)
204 {
205 gint ret;
206 PolkitAuthority *authority;
207 PolkitSubject *session;
208 GError *error;
209
210 ret = 1;
211 authority = NULL;
212 session = NULL;
213
214 error = NULL;
215 authority = polkit_authority_get_sync (NULL /* GCancellable* */, &error);
216 if (authority == NULL)
217 {
218 g_printerr ("Error getting authority: %s\n", error->message);
219 g_error_free (error);
220 goto out;
221 }
222
223 error = NULL;
224 session = polkit_unix_session_new_for_process_sync (getpid (),
225 NULL, /* GCancellable */
226 &error);
227 if (session == NULL)
228 {
229 g_printerr ("Error getting session: %s\n", error->message);
230 g_error_free (error);
231 goto out;
232 }
233
234 if (revoke)
235 {
236 if (!polkit_authority_revoke_temporary_authorizations_sync (authority,
237 session,
238 NULL, /* GCancellable */
239 &error))
240 {
241 g_printerr ("Error revoking temporary authorizations: %s\n", error->message);
242 g_error_free (error);
243 goto out;
244 }
245
246 ret = 0;
247 }
248 else
249 {
250 GList *authorizations;
251 GList *l;
252
253 error = NULL;
254 authorizations = polkit_authority_enumerate_temporary_authorizations_sync (authority,
255 session,
256 NULL, /* GCancellable */
257 &error);
258 if (error != NULL)
259 {
260 g_printerr ("Error getting temporary authorizations: %s\n", error->message);
261 g_error_free (error);
262 goto out;
263 }
264
265 for (l = authorizations; l != NULL; l = l->next)
266 {
267 PolkitTemporaryAuthorization *a = POLKIT_TEMPORARY_AUTHORIZATION (l->data);
268 const gchar *id;
269 const gchar *action_id;
270 PolkitSubject *subject;
271 gchar *subject_cmdline;
272 time_t obtained;
273 time_t expires;
274 GTimeVal now;
275 gchar *subject_str;
276 gchar obtained_str[64];
277 gchar expires_str[64];
278 gchar *obtained_rel_str;
279 gchar *expires_rel_str;
280 struct tm *broken_down;
281
282 id = polkit_temporary_authorization_get_id (a);
283 action_id = polkit_temporary_authorization_get_action_id (a);
284 subject = polkit_temporary_authorization_get_subject (a);
285 subject_str = polkit_subject_to_string (subject);
286 subject_cmdline = _polkit_subject_get_cmdline (subject);
287 obtained = polkit_temporary_authorization_get_time_obtained (a);
288 expires = polkit_temporary_authorization_get_time_expires (a);
289
290 g_get_current_time (&now);
291
292 broken_down = localtime (&obtained);
293 strftime (obtained_str, sizeof (obtained_str), "%c", broken_down);
294 broken_down = localtime (&expires);
295 strftime (expires_str, sizeof (expires_str), "%c", broken_down);
296
297 obtained_rel_str = format_reltime (obtained - now.tv_sec);
298 expires_rel_str = format_reltime (expires - now.tv_sec);
299
300 g_print ("authorization id: %s\n"
301 "action: %s\n"
302 "subject: %s (%s)\n"
303 "obtained: %s (%s)\n"
304 "expires: %s (%s)\n"
305 "\n",
306 id,
307 action_id,
308 subject_str, subject_cmdline != NULL ? subject_cmdline : "cannot read cmdline",
309 obtained_rel_str, obtained_str,
310 expires_rel_str, expires_str);
311
312 g_object_unref (subject);
313 g_free (subject_str);
314 g_free (subject_cmdline);
315 g_free (obtained_rel_str);
316 g_free (expires_rel_str);
317 }
318 g_list_foreach (authorizations, (GFunc) g_object_unref, NULL);
319 g_list_free (authorizations);
320
321 ret = 0;
322 }
323
324 out:
325 if (authority != NULL)
326 g_object_unref (authority);
327 if (session != NULL)
328 g_object_unref (session);
329
330 return ret;
331 }
332
333 int
main(int argc,char * argv[])334 main (int argc, char *argv[])
335 {
336 guint n;
337 guint ret;
338 gchar *action_id;
339 gboolean opt_show_help;
340 gboolean opt_show_version;
341 gboolean allow_user_interaction;
342 gboolean enable_internal_agent;
343 gboolean list_temp;
344 gboolean revoke_temp;
345 PolkitAuthority *authority;
346 PolkitAuthorizationResult *result;
347 PolkitSubject *subject;
348 PolkitDetails *details;
349 PolkitCheckAuthorizationFlags flags;
350 PolkitDetails *result_details;
351 GError *error;
352 gpointer local_agent_handle;
353
354 subject = NULL;
355 action_id = NULL;
356 details = NULL;
357 authority = NULL;
358 result = NULL;
359 allow_user_interaction = FALSE;
360 enable_internal_agent = FALSE;
361 list_temp = FALSE;
362 revoke_temp = FALSE;
363 local_agent_handle = NULL;
364 ret = 126;
365
366 if (argc < 1)
367 {
368 exit(126);
369 }
370
371 /* Disable remote file access from GIO. */
372 setenv ("GIO_USE_VFS", "local", 1);
373
374 details = polkit_details_new ();
375
376 opt_show_help = FALSE;
377 opt_show_version = FALSE;
378 g_set_prgname ("pkcheck");
379 for (n = 1; n < (guint) argc; n++)
380 {
381 if (g_strcmp0 (argv[n], "--help") == 0)
382 {
383 opt_show_help = TRUE;
384 }
385 else if (g_strcmp0 (argv[n], "--version") == 0)
386 {
387 opt_show_version = TRUE;
388 }
389 else if (g_strcmp0 (argv[n], "--process") == 0 || g_strcmp0 (argv[n], "-p") == 0)
390 {
391 gint pid;
392 guint uid;
393 guint64 pid_start_time;
394
395 n++;
396 if (n >= (guint) argc)
397 {
398 g_printerr (_("%s: Argument expected after `%s'\n"),
399 g_get_prgname (), "--process");
400 goto out;
401 }
402
403 if (sscanf (argv[n], "%i,%" G_GUINT64_FORMAT ",%u", &pid, &pid_start_time, &uid) == 3)
404 {
405 subject = polkit_unix_process_new_for_owner (pid, pid_start_time, uid);
406 }
407 else if (sscanf (argv[n], "%i,%" G_GUINT64_FORMAT, &pid, &pid_start_time) == 2)
408 {
409 G_GNUC_BEGIN_IGNORE_DEPRECATIONS
410 subject = polkit_unix_process_new_full (pid, pid_start_time);
411 G_GNUC_END_IGNORE_DEPRECATIONS
412 }
413 else if (sscanf (argv[n], "%i", &pid) == 1)
414 {
415 G_GNUC_BEGIN_IGNORE_DEPRECATIONS
416 subject = polkit_unix_process_new (pid);
417 G_GNUC_END_IGNORE_DEPRECATIONS
418 }
419 else
420 {
421 g_printerr (_("%s: Invalid --process value `%s'\n"),
422 g_get_prgname (), argv[n]);
423 goto out;
424 }
425 }
426 else if (g_strcmp0 (argv[n], "--system-bus-name") == 0 || g_strcmp0 (argv[n], "-s") == 0)
427 {
428 n++;
429 if (n >= (guint) argc)
430 {
431 g_printerr (_("%s: Argument expected after `%s'\n"),
432 g_get_prgname (), "--system-bus-name");
433 goto out;
434 }
435
436 subject = polkit_system_bus_name_new (argv[n]);
437 }
438 else if (g_strcmp0 (argv[n], "--action-id") == 0 || g_strcmp0 (argv[n], "-a") == 0)
439 {
440 n++;
441 if (n >= (guint) argc)
442 {
443 g_printerr (_("%s: Argument expected after `%s'\n"),
444 g_get_prgname (), "--action-id");
445 goto out;
446 }
447
448 action_id = g_strdup (argv[n]);
449 }
450 else if (g_strcmp0 (argv[n], "--detail") == 0 || g_strcmp0 (argv[n], "-d") == 0)
451 {
452 const gchar *key;
453 const gchar *value;
454
455 n++;
456 if (n >= (guint) argc)
457 {
458 g_printerr (_("%s: Two arguments expected after `--detail'\n"),
459 g_get_prgname ());
460 goto out;
461 }
462 key = argv[n];
463
464 n++;
465 if (n >= (guint) argc)
466 {
467 g_printerr (_("%s: Two arguments expected after `--detail'\n"),
468 g_get_prgname ());
469 goto out;
470 }
471 value = argv[n];
472
473 polkit_details_insert (details, key, value);
474 }
475 else if (g_strcmp0 (argv[n], "--allow-user-interaction") == 0 || g_strcmp0 (argv[n], "-u") == 0)
476 {
477 allow_user_interaction = TRUE;
478 }
479 else if (g_strcmp0 (argv[n], "--enable-internal-agent") == 0)
480 {
481 enable_internal_agent = TRUE;
482 }
483 else if (g_strcmp0 (argv[n], "--list-temp") == 0)
484 {
485 list_temp = TRUE;
486 }
487 else if (g_strcmp0 (argv[n], "--revoke-temp") == 0)
488 {
489 revoke_temp = TRUE;
490 }
491 else
492 {
493 break;
494 }
495 }
496 if (argv[n] != NULL)
497 {
498 g_printerr (_("%s: Unexpected argument `%s'\n"), g_get_prgname (),
499 argv[n]);
500 goto out;
501 }
502
503 if (opt_show_help)
504 {
505 help ();
506 ret = 0;
507 goto out;
508 }
509 else if (opt_show_version)
510 {
511 g_print ("pkcheck version %s\n", PACKAGE_VERSION);
512 ret = 0;
513 goto out;
514 }
515
516 if (list_temp)
517 {
518 ret = do_list_or_revoke_temp_authz (FALSE);
519 goto out;
520 }
521 else if (revoke_temp)
522 {
523 ret = do_list_or_revoke_temp_authz (TRUE);
524 goto out;
525 }
526 else if (subject == NULL)
527 {
528 g_printerr (_("%s: Subject not specified\n"), g_get_prgname ());
529 goto out;
530 }
531
532 error = NULL;
533 authority = polkit_authority_get_sync (NULL /* GCancellable* */, &error);
534 if (authority == NULL)
535 {
536 g_printerr ("Error getting authority: %s\n", error->message);
537 g_error_free (error);
538 goto out;
539 }
540
541 try_again:
542 error = NULL;
543 flags = POLKIT_CHECK_AUTHORIZATION_FLAGS_NONE;
544 if (allow_user_interaction)
545 flags |= POLKIT_CHECK_AUTHORIZATION_FLAGS_ALLOW_USER_INTERACTION;
546 result = polkit_authority_check_authorization_sync (authority,
547 subject,
548 action_id,
549 details,
550 flags,
551 NULL,
552 &error);
553 if (result == NULL)
554 {
555 g_printerr ("Error checking for authorization %s: %s\n",
556 action_id,
557 error->message);
558 ret = 127;
559 goto out;
560 }
561
562 result_details = polkit_authorization_result_get_details (result);
563 if (result_details != NULL)
564 {
565 gchar **keys;
566
567 keys = polkit_details_get_keys (result_details);
568 for (n = 0; keys != NULL && keys[n] != NULL; n++)
569 {
570 const gchar *key;
571 const gchar *value;
572 gchar *s;
573
574 key = keys[n];
575 value = polkit_details_lookup (result_details, key);
576
577 s = escape_str (key);
578 g_print ("%s", s);
579 g_free (s);
580 g_print ("=");
581 s = escape_str (value);
582 g_print ("%s", s);
583 g_free (s);
584 g_print ("\n");
585 }
586
587 g_strfreev (keys);
588 }
589
590 if (polkit_authorization_result_get_is_authorized (result))
591 {
592 ret = 0;
593 }
594 else if (polkit_authorization_result_get_is_challenge (result))
595 {
596 if (allow_user_interaction)
597 {
598 if (local_agent_handle == NULL && enable_internal_agent)
599 {
600 PolkitAgentListener *listener;
601 error = NULL;
602 /* this will fail if we can't find a controlling terminal */
603 listener = polkit_agent_text_listener_new (NULL, &error);
604 if (listener == NULL)
605 {
606 g_printerr ("Error creating textual authentication agent: %s\n", error->message);
607 g_error_free (error);
608 goto out;
609 }
610 local_agent_handle = polkit_agent_listener_register (listener,
611 POLKIT_AGENT_REGISTER_FLAGS_RUN_IN_THREAD,
612 subject,
613 NULL, /* object_path */
614 NULL, /* GCancellable */
615 &error);
616 g_object_unref (listener);
617 if (local_agent_handle == NULL)
618 {
619 g_printerr ("Error registering local authentication agent: %s\n", error->message);
620 g_error_free (error);
621 goto out;
622 }
623 g_object_unref (result);
624 result = NULL;
625 goto try_again;
626 }
627 else
628 {
629 g_printerr ("Authorization requires authentication but no agent is available.\n");
630 }
631 }
632 else
633 {
634 g_printerr ("Authorization requires authentication and -u wasn't passed.\n");
635 }
636 ret = 2;
637 }
638 else if (polkit_authorization_result_get_dismissed (result))
639 {
640 g_printerr ("Authentication request was dismissed.\n");
641 ret = 3;
642 }
643 else
644 {
645 g_printerr ("Not authorized.\n");
646 ret = 1;
647 }
648
649 out:
650 /* if applicable, nuke the local authentication agent */
651 if (local_agent_handle != NULL)
652 polkit_agent_listener_unregister (local_agent_handle);
653
654 if (result != NULL)
655 g_object_unref (result);
656
657 g_free (action_id);
658
659 if (details != NULL)
660 g_object_unref (details);
661
662 if (subject != NULL)
663 g_object_unref (subject);
664
665 if (authority != NULL)
666 g_object_unref (authority);
667
668 return ret;
669 }
670