Include polkit_error details in exception (bnc#679127#c8).
[opensuse:ruby-polkit.git] / ext / polkit / polkit.c
1 /*
2  * polkit.c
3  *
4  * Minimal Ruby extension to check if a specific action
5  * is allowed through PolicyKit
6  * 
7  */
8  
9 #include <stdlib.h>
10 #include <stdio.h>
11 #include <unistd.h>
12
13 #include <dbus/dbus.h>
14 #include <polkit-dbus/polkit-dbus.h>
15
16 #include <sys/types.h>
17
18
19 #include <ruby.h>
20
21 /* Ruby module */
22 static VALUE mPolKit = Qnil;
23
24
25 /**
26  * checks if user can perform action
27  * \param action:string action (dbus-style resource string) which user wants do
28  * \param user:fixnum uid of user
29  * \return symbol
30  *         :yes if user has permission
31  *         :auth if authorization required
32  *         :no if permision denied
33  *         raises exception on error
34  */
35 VALUE
36 method_polkit_check(VALUE self, VALUE act_v, VALUE usr_v)
37 {
38     const char *action_s = StringValuePtr(act_v);
39     uid_t uid = NUM2ULONG(usr_v);
40     const char *error = NULL;
41     VALUE ret = Qnil;
42
43     DBusError dbus_error;
44     DBusConnection *bus = NULL;
45     PolKitCaller *caller = NULL;
46     PolKitAction *action = NULL;
47     PolKitContext *context = NULL;
48     PolKitError *polkit_error = NULL;
49     PolKitResult polkit_result;
50     VALUE ruby_error = Qnil;
51
52     /*
53      * Connect to PolicyKit via DBus
54      */
55     dbus_error_init(&dbus_error);
56     if (!(bus = dbus_bus_get(DBUS_BUS_SYSTEM, &dbus_error))) {
57         error = "DBus connect failed";
58         goto finish;
59     }
60     if (!(caller = polkit_caller_new_from_pid(bus, getpid(), &dbus_error))) {
61         error = "PolicyKit connect failed";
62         goto finish;
63     }
64
65     if (!(polkit_caller_set_uid(caller, uid))) {
66         error = "Can't set PolicyKit caller uid";
67         goto finish;
68     } 
69
70     if (!(action = polkit_action_new())) {
71         error = "Can't create PolicyKit action";
72         goto finish;
73     }
74
75     if (!polkit_action_set_action_id(action, action_s)) {
76         error = "Can't set PolicyKit action";
77         goto finish;
78     }
79
80     if (!(context = polkit_context_new())) {
81         error = "Can't create PolicyKit context";
82         goto finish;
83     }
84
85     if (!polkit_context_init(context, &polkit_error)) {
86         error = "Can't initialize PolicyKit context";
87         goto finish;
88     }
89
90     polkit_result = polkit_context_is_caller_authorized(context, action, caller, FALSE, &polkit_error);
91
92     if (polkit_error_is_set(polkit_error)) {
93         VALUE message = rb_str_buf_new2("PolicyKit failed: ");
94         rb_str_buf_cat2(message, polkit_error_get_error_name(polkit_error));
95         rb_str_buf_cat(message, ": ", 2);
96         rb_str_buf_cat2(message, polkit_error_get_error_message(polkit_error));
97                         
98         ruby_error = rb_exc_new3(rb_eRuntimeError, message);
99         goto finish;
100     }
101     
102     switch (polkit_result)
103     {
104         case POLKIT_RESULT_ONLY_VIA_ADMIN_AUTH:
105         case POLKIT_RESULT_ONLY_VIA_ADMIN_AUTH_KEEP_SESSION:
106         case POLKIT_RESULT_ONLY_VIA_ADMIN_AUTH_KEEP_ALWAYS:
107         case POLKIT_RESULT_ONLY_VIA_ADMIN_AUTH_ONE_SHOT:
108         case POLKIT_RESULT_ONLY_VIA_SELF_AUTH:
109         case POLKIT_RESULT_ONLY_VIA_SELF_AUTH_KEEP_SESSION:
110         case POLKIT_RESULT_ONLY_VIA_SELF_AUTH_KEEP_ALWAYS:
111         case POLKIT_RESULT_ONLY_VIA_SELF_AUTH_ONE_SHOT:
112             ret = ID2SYM(rb_intern("auth"));
113             break;
114         case POLKIT_RESULT_YES:
115             ret = ID2SYM(rb_intern("yes"));
116             break;
117         case POLKIT_RESULT_NO:
118             ret = ID2SYM(rb_intern("no"));
119             break;
120         default:
121             error = "Unhandled PolicyKit value";
122             break;
123     }
124
125  finish:
126
127     if (caller)
128         polkit_caller_unref(caller);
129
130     if (action)
131         polkit_action_unref(action);
132
133     if (context)
134         polkit_context_unref(context);
135
136     if (bus)
137         dbus_connection_unref(bus);
138
139     dbus_error_free(&dbus_error);
140
141     if (polkit_error)
142         polkit_error_free(polkit_error);
143
144     if (!NIL_P(ruby_error))
145       rb_exc_raise(ruby_error);
146
147     if (error)
148       rb_raise(rb_eRuntimeError, error);
149
150     return ret;
151 }
152
153
154 /* The initialization method for this module */
155 void
156 Init_polkit()
157 {
158     mPolKit = rb_define_module("PolKit");
159     rb_define_module_function(mPolKit, "polkit_check_uid", method_polkit_check, 2);     
160 }