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