Location | Tool | Test ID | Function | Issue |
---|---|---|---|---|
shell-polkit-authentication-agent.c:259:11 | clang-analyzer | Access to field 'g_class' results in a dereference of a null pointer (loaded from field 'data') |
1 /* -*- mode: C; c-file-style: "gnu"; indent-tabs-mode: nil; -*- */
2
3 /*
4 * Copyright (C) 20011 Red Hat, Inc.
5 *
6 * Author: David Zeuthen <davidz@redhat.com>
7 */
8
9 #include "config.h"
10
11 #include <pwd.h>
12
13 #define POLKIT_AGENT_I_KNOW_API_IS_SUBJECT_TO_CHANGE
14 #include <polkitagent/polkitagent.h>
15 #include "shell-polkit-authentication-agent.h"
16
17 #include <glib/gi18n.h>
18
19 /* uncomment for useful debug output */
20 /* #define SHOW_DEBUG */
21
22 #ifdef SHOW_DEBUG
23 static void
24 print_debug (const gchar *format, ...)
25 {
26 gchar *s;
27 va_list ap;
28 gchar timebuf[64];
29 GTimeVal now;
30 time_t now_t;
31 struct tm broken_down;
32
33 g_get_current_time (&now);
34 now_t = now.tv_sec;
35 localtime_r (&now_t, &broken_down);
36 strftime (timebuf, sizeof timebuf, "%H:%M:%S", &broken_down);
37
38 va_start (ap, format);
39 s = g_strdup_vprintf (format, ap);
40 va_end (ap);
41
42 g_print ("ShellPolkitAuthenticationAgent: %s.%03d: %s\n", timebuf, (gint) (now.tv_usec / 1000), s);
43 g_free (s);
44 }
45 #else
46 static void
47 print_debug (const gchar *str, ...)
48 {
49 }
50 #endif
51
52
53 struct _ShellPolkitAuthenticationAgentClass
54 {
55 PolkitAgentListenerClass parent_class;
56 };
57
58 struct _AuthRequest;
59 typedef struct _AuthRequest AuthRequest;
60
61 struct _ShellPolkitAuthenticationAgent {
62 PolkitAgentListener parent_instance;
63
64 GList *scheduled_requests;
65 AuthRequest *current_request;
66
67 gpointer handle;
68 };
69
70 /* Signals */
71 enum
72 {
73 INITIATE_SIGNAL,
74 CANCEL_SIGNAL,
75 LAST_SIGNAL
76 };
77
78 static guint signals[LAST_SIGNAL] = { 0 };
79
80 G_DEFINE_TYPE (ShellPolkitAuthenticationAgent, shell_polkit_authentication_agent, POLKIT_AGENT_TYPE_LISTENER);
81
82 static void initiate_authentication (PolkitAgentListener *listener,
83 const gchar *action_id,
84 const gchar *message,
85 const gchar *icon_name,
86 PolkitDetails *details,
87 const gchar *cookie,
88 GList *identities,
89 GCancellable *cancellable,
90 GAsyncReadyCallback callback,
91 gpointer user_data);
92
93 static gboolean initiate_authentication_finish (PolkitAgentListener *listener,
94 GAsyncResult *res,
95 GError **error);
96
97 void
98 shell_polkit_authentication_agent_init (ShellPolkitAuthenticationAgent *agent)
99 {
100 }
101
102 void
103 shell_polkit_authentication_agent_register (ShellPolkitAuthenticationAgent *agent,
104 GError **error_out)
105 {
106 GError *error = NULL;
107 PolkitSubject *subject;
108
109 subject = polkit_unix_session_new_for_process_sync (getpid (),
110 NULL, /* GCancellable* */
111 &error);
112 if (subject == NULL)
113 {
114 if (error == NULL) /* polkit version 104 and older don't properly set error on failure */
115 error = g_error_new (POLKIT_ERROR, POLKIT_ERROR_FAILED,
116 "PolKit failed to properly get our session");
117 goto out;
118 }
119
120 agent->handle = polkit_agent_listener_register (POLKIT_AGENT_LISTENER (agent),
121 POLKIT_AGENT_REGISTER_FLAGS_NONE,
122 subject,
123 NULL, /* use default object path */
124 NULL, /* GCancellable */
125 &error);
126
127 out:
128 if (error != NULL)
129 g_propagate_error (error_out, error);
130
131 if (subject != NULL)
132 g_object_unref (subject);
133 }
134
135 static void
136 shell_polkit_authentication_agent_finalize (GObject *object)
137 {
138 ShellPolkitAuthenticationAgent *agent = SHELL_POLKIT_AUTHENTICATION_AGENT (object);
139 shell_polkit_authentication_agent_unregister (agent);
140 G_OBJECT_CLASS (shell_polkit_authentication_agent_parent_class)->finalize (object);
141 }
142
143 static void
144 shell_polkit_authentication_agent_class_init (ShellPolkitAuthenticationAgentClass *klass)
145 {
146 GObjectClass *gobject_class;
147 PolkitAgentListenerClass *listener_class;
148
149 gobject_class = G_OBJECT_CLASS (klass);
150 gobject_class->finalize = shell_polkit_authentication_agent_finalize;
151
152 listener_class = POLKIT_AGENT_LISTENER_CLASS (klass);
153 listener_class->initiate_authentication = initiate_authentication;
154 listener_class->initiate_authentication_finish = initiate_authentication_finish;
155
156 signals[INITIATE_SIGNAL] =
157 g_signal_new ("initiate",
158 G_TYPE_FROM_CLASS (klass),
159 G_SIGNAL_RUN_LAST,
160 0, /* class_offset */
161 NULL, /* accumulator */
162 NULL, /* accumulator data */
163 NULL, /* marshaller */
164 G_TYPE_NONE,
165 5,
166 G_TYPE_STRING,
167 G_TYPE_STRING,
168 G_TYPE_STRING,
169 G_TYPE_STRING,
170 G_TYPE_STRV);
171
172 signals[CANCEL_SIGNAL] =
173 g_signal_new ("cancel",
174 G_TYPE_FROM_CLASS (klass),
175 G_SIGNAL_RUN_LAST,
176 0, /* class_offset */
177 NULL, /* accumulator */
178 NULL, /* accumulator data */
179 NULL, /* marshaller */
180 G_TYPE_NONE,
181 0);
182 }
183
184 ShellPolkitAuthenticationAgent *
185 shell_polkit_authentication_agent_new (void)
186 {
187 return SHELL_POLKIT_AUTHENTICATION_AGENT (g_object_new (SHELL_TYPE_POLKIT_AUTHENTICATION_AGENT, NULL));
188 }
189
190 struct _AuthRequest {
191 /* not holding ref */
192 ShellPolkitAuthenticationAgent *agent;
193 GCancellable *cancellable;
194 gulong handler_id;
195
196 /* copies */
197 gchar *action_id;
198 gchar *message;
199 gchar *icon_name;
200 PolkitDetails *details;
201 gchar *cookie;
202 GList *identities;
203
204 GSimpleAsyncResult *simple;
205 };
206
207 static void
208 auth_request_free (AuthRequest *request)
209 {
210 g_cancellable_disconnect (request->cancellable, request->handler_id);
211 g_free (request->action_id);
212 g_free (request->message);
213 g_free (request->icon_name);
214 g_object_unref (request->details);
215 g_free (request->cookie);
216 g_list_foreach (request->identities, (GFunc) g_object_unref, NULL);
217 g_list_free (request->identities);
218 g_object_unref (request->simple);
219 g_free (request);
220 }
221
222 static void
223 auth_request_initiate (AuthRequest *request)
224 {
225 gchar **user_names;
226 GPtrArray *p;
227 GList *l;
228
229 p = g_ptr_array_new ();
230 for (l = request->identities; l != NULL; l = l->next)
231 {
232 if (POLKIT_IS_UNIX_USER (l->data))
233 {
234 PolkitUnixUser *user = POLKIT_UNIX_USER (l->data);
235 gint uid;
236 gchar buf[4096];
237 struct passwd pwd;
238 struct passwd *ppwd;
239
240 uid = polkit_unix_user_get_uid (user);
241 if (getpwuid_r (uid, &pwd, buf, sizeof (buf), &ppwd) == 0)
242 {
243 if (!g_utf8_validate (pwd.pw_name, -1, NULL))
244 {
245 g_warning ("Invalid UTF-8 in username for uid %d. Skipping", uid);
246 }
247 else
248 {
249 g_ptr_array_add (p, g_strdup (pwd.pw_name));
250 }
251 }
252 else
253 {
254 g_warning ("Error looking up user name for uid %d", uid);
255 }
256 }
257 else
258 {
259 g_warning ("Unsupporting identity of GType %s", g_type_name (G_TYPE_FROM_INSTANCE (l->data)));
(emitted by clang-analyzer)TODO: a detailed trace is available in the data model (not yet rendered in this report)
260 }
261 }
262 g_ptr_array_add (p, NULL);
263 user_names = (gchar **) g_ptr_array_free (p, FALSE);
264 g_signal_emit (request->agent,
265 signals[INITIATE_SIGNAL],
266 0, /* detail */
267 request->action_id,
268 request->message,
269 request->icon_name,
270 request->cookie,
271 user_names);
272 g_strfreev (user_names);
273 }
274
275 static void auth_request_complete (AuthRequest *request,
276 gboolean dismissed);
277
278 static gboolean
279 handle_cancelled_in_idle (gpointer user_data)
280 {
281 AuthRequest *request = user_data;
282
283 print_debug ("CANCELLED %s cookie %s", request->action_id, request->cookie);
284 if (request == request->agent->current_request)
285 {
286 g_signal_emit (request->agent,
287 signals[CANCEL_SIGNAL],
288 0); /* detail */
289 }
290 else
291 {
292 auth_request_complete (request, FALSE);
293 }
294
295 return FALSE;
296 }
297
298 static void
299 on_request_cancelled (GCancellable *cancellable,
300 gpointer user_data)
301 {
302 AuthRequest *request = user_data;
303 /* post-pone to idle to handle GCancellable deadlock in
304 *
305 * https://bugzilla.gnome.org/show_bug.cgi?id=642968
306 */
307 g_idle_add (handle_cancelled_in_idle, request);
308 }
309
310 static void
311 auth_request_dismiss (AuthRequest *request)
312 {
313 auth_request_complete (request, TRUE);
314 }
315
316 void
317 shell_polkit_authentication_agent_unregister (ShellPolkitAuthenticationAgent *agent)
318 {
319 if (agent->scheduled_requests != NULL)
320 {
321 g_list_foreach (agent->scheduled_requests, (GFunc)auth_request_dismiss, NULL);
322 agent->scheduled_requests = NULL;
323 }
324 if (agent->current_request != NULL)
325 auth_request_dismiss (agent->current_request);
326
327 polkit_agent_listener_unregister (agent->handle);
328 agent->handle = NULL;
329 }
330
331 static void maybe_process_next_request (ShellPolkitAuthenticationAgent *agent);
332
333 static void
334 auth_request_complete (AuthRequest *request,
335 gboolean dismissed)
336 {
337 ShellPolkitAuthenticationAgent *agent = request->agent;
338
339 if (dismissed)
340 g_simple_async_result_set_error (request->simple,
341 POLKIT_ERROR,
342 POLKIT_ERROR_CANCELLED,
343 _("Authentication dialog was dismissed by the user"));
344
345 if (agent->current_request == request)
346 {
347 print_debug ("COMPLETING CURRENT %s cookie %s", request->action_id, request->cookie);
348
349 g_simple_async_result_complete_in_idle (request->simple);
350 auth_request_free (request);
351
352 agent->current_request = NULL;
353
354 maybe_process_next_request (agent);
355 }
356 else
357 {
358 print_debug ("COMPLETING SCHEDULED %s cookie %s", request->action_id, request->cookie);
359 agent->scheduled_requests = g_list_remove (agent->scheduled_requests, request);
360 g_simple_async_result_complete_in_idle (request->simple);
361 auth_request_free (request);
362 }
363 }
364
365 static void
366 maybe_process_next_request (ShellPolkitAuthenticationAgent *agent)
367 {
368 print_debug ("MAYBE_PROCESS cur=%p len(scheduled)=%d", agent->current_request, g_list_length (agent->scheduled_requests));
369
370 if (agent->current_request == NULL && agent->scheduled_requests != NULL)
371 {
372 AuthRequest *request;
373
374 request = agent->scheduled_requests->data;
375
376 agent->current_request = request;
377 agent->scheduled_requests = g_list_remove (agent->scheduled_requests, request);
378
379 print_debug ("INITIATING %s cookie %s", request->action_id, request->cookie);
380 auth_request_initiate (request);
381 }
382 }
383
384 static void
385 initiate_authentication (PolkitAgentListener *listener,
386 const gchar *action_id,
387 const gchar *message,
388 const gchar *icon_name,
389 PolkitDetails *details,
390 const gchar *cookie,
391 GList *identities,
392 GCancellable *cancellable,
393 GAsyncReadyCallback callback,
394 gpointer user_data)
395 {
396 ShellPolkitAuthenticationAgent *agent = SHELL_POLKIT_AUTHENTICATION_AGENT (listener);
397 AuthRequest *request;
398
399 request = g_new0 (AuthRequest, 1);
400 request->agent = agent;
401 request->action_id = g_strdup (action_id);
402 request->message = g_strdup (message);
403 request->icon_name = g_strdup (icon_name);
404 request->details = g_object_ref (details);
405 request->cookie = g_strdup (cookie);
406 request->identities = g_list_copy (identities);
407 g_list_foreach (request->identities, (GFunc) g_object_ref, NULL);
408 request->simple = g_simple_async_result_new (G_OBJECT (listener),
409 callback,
410 user_data,
411 initiate_authentication);
412 request->cancellable = cancellable;
413 request->handler_id = g_cancellable_connect (request->cancellable,
414 G_CALLBACK (on_request_cancelled),
415 request,
416 NULL); /* GDestroyNotify for request */
417
418 print_debug ("SCHEDULING %s cookie %s", request->action_id, request->cookie);
419 agent->scheduled_requests = g_list_append (agent->scheduled_requests, request);
420
421 maybe_process_next_request (agent);
422 }
423
424 static gboolean
425 initiate_authentication_finish (PolkitAgentListener *listener,
426 GAsyncResult *res,
427 GError **error)
428 {
429 GSimpleAsyncResult *simple = G_SIMPLE_ASYNC_RESULT (res);
430 if (g_simple_async_result_propagate_error (simple, error))
431 return FALSE;
432 else
433 return TRUE;
434 }
435
436 void
437 shell_polkit_authentication_agent_complete (ShellPolkitAuthenticationAgent *agent,
438 gboolean dismissed)
439 {
440 g_return_if_fail (SHELL_IS_POLKIT_AUTHENTICATION_AGENT (agent));
441 g_return_if_fail (agent->current_request != NULL);
442
443 auth_request_complete (agent->current_request, dismissed);
444 }