101
by Robert Ancell
Make a D-Bus service to allow non-root users to login |
1 |
/*
|
2 |
* Copyright (C) 2016 Canonical Ltd.
|
|
3 |
*
|
|
4 |
* This library is free software; you can redistribute it and/or modify it under
|
|
5 |
* the terms of the GNU Lesser General Public License as published by the Free
|
|
6 |
* Software Foundation; either version 2 or version 3 of the License.
|
|
7 |
* See http://www.gnu.org/copyleft/lgpl.html the full text of the license.
|
|
8 |
*/
|
|
9 |
||
10 |
#include <stdlib.h> |
|
11 |
#include <gio/gio.h> |
|
12 |
#include <snapd-glib/snapd-glib.h> |
|
13 |
#include <polkit/polkit.h> |
|
14 |
||
15 |
#include "login-service.h" |
|
16 |
||
17 |
#if !defined(POLKIT_HAS_AUTOPTR_MACROS)
|
|
18 |
G_DEFINE_AUTOPTR_CLEANUP_FUNC(PolkitAuthorizationResult, g_object_unref) |
|
19 |
G_DEFINE_AUTOPTR_CLEANUP_FUNC(PolkitSubject, g_object_unref) |
|
20 |
#endif
|
|
21 |
||
22 |
static GMainLoop *loop; |
|
23 |
static int result = EXIT_SUCCESS; |
|
24 |
||
25 |
static PolkitAuthority *authority = NULL; |
|
26 |
static IoSnapcraftSnapdLoginService *service = NULL; |
|
27 |
||
28 |
typedef struct |
|
29 |
{
|
|
30 |
GDBusMethodInvocation *invocation; |
|
31 |
gchar *username; |
|
32 |
gchar *password; |
|
33 |
gchar *otp; |
|
34 |
GPermission *permission; |
|
35 |
SnapdClient *client; |
|
36 |
} LoginRequest; |
|
37 |
||
38 |
static void |
|
39 |
free_login_request (LoginRequest *request) |
|
40 |
{
|
|
41 |
g_object_unref (request->invocation); |
|
42 |
g_free (request->username); |
|
43 |
g_free (request->password); |
|
44 |
g_free (request->otp); |
|
45 |
if (request->permission != NULL) |
|
46 |
g_object_unref (request->permission); |
|
47 |
if (request->client != NULL) |
|
48 |
g_object_unref (request->client); |
|
49 |
g_slice_free (LoginRequest, request); |
|
50 |
}
|
|
51 |
||
52 |
G_DEFINE_AUTOPTR_CLEANUP_FUNC (LoginRequest, free_login_request); |
|
53 |
||
54 |
static void |
|
116
by Robert Ancell
Fix errors not being correct on first use of snapd_login |
55 |
return_error (LoginRequest *request, GError *error) |
56 |
{
|
|
57 |
if (error->domain != SNAPD_ERROR) { |
|
58 |
g_dbus_method_invocation_return_gerror (request->invocation, error); |
|
59 |
return; |
|
60 |
}
|
|
61 |
||
62 |
switch (error->code) { |
|
63 |
case SNAPD_ERROR_CONNECTION_FAILED: |
|
64 |
g_dbus_method_invocation_return_dbus_error (request->invocation, "io.snapcraft.SnapdLoginService.Error.ConnectionFailed", error->message); |
|
65 |
break; |
|
123
by Robert Ancell
Rename error codes |
66 |
case SNAPD_ERROR_WRITE_FAILED: |
67 |
g_dbus_method_invocation_return_dbus_error (request->invocation, "io.snapcraft.SnapdLoginService.Error.WriteFailed", error->message); |
|
68 |
break; |
|
69 |
case SNAPD_ERROR_READ_FAILED: |
|
70 |
g_dbus_method_invocation_return_dbus_error (request->invocation, "io.snapcraft.SnapdLoginService.Error.ReadFailed", error->message); |
|
71 |
break; |
|
72 |
case SNAPD_ERROR_BAD_REQUEST: |
|
73 |
g_dbus_method_invocation_return_dbus_error (request->invocation, "io.snapcraft.SnapdLoginService.Error.BadRequest", error->message); |
|
74 |
break; |
|
75 |
case SNAPD_ERROR_BAD_RESPONSE: |
|
76 |
g_dbus_method_invocation_return_dbus_error (request->invocation, "io.snapcraft.SnapdLoginService.Error.BadResponse", error->message); |
|
77 |
break; |
|
78 |
case SNAPD_ERROR_AUTH_DATA_REQUIRED: |
|
79 |
g_dbus_method_invocation_return_dbus_error (request->invocation, "io.snapcraft.SnapdLoginService.Error.AuthDataRequired", error->message); |
|
80 |
break; |
|
81 |
case SNAPD_ERROR_AUTH_DATA_INVALID: |
|
82 |
g_dbus_method_invocation_return_dbus_error (request->invocation, "io.snapcraft.SnapdLoginService.Error.AuthDataInvalid", error->message); |
|
116
by Robert Ancell
Fix errors not being correct on first use of snapd_login |
83 |
break; |
84 |
case SNAPD_ERROR_TWO_FACTOR_REQUIRED: |
|
85 |
g_dbus_method_invocation_return_dbus_error (request->invocation, "io.snapcraft.SnapdLoginService.Error.TwoFactorRequired", error->message); |
|
86 |
break; |
|
123
by Robert Ancell
Rename error codes |
87 |
case SNAPD_ERROR_TWO_FACTOR_INVALID: |
88 |
g_dbus_method_invocation_return_dbus_error (request->invocation, "io.snapcraft.SnapdLoginService.Error.TwoFactorInvalid", error->message); |
|
116
by Robert Ancell
Fix errors not being correct on first use of snapd_login |
89 |
break; |
90 |
case SNAPD_ERROR_PERMISSION_DENIED: |
|
91 |
g_dbus_method_invocation_return_dbus_error (request->invocation, "io.snapcraft.SnapdLoginService.Error.PermissionDenied", error->message); |
|
92 |
break; |
|
123
by Robert Ancell
Rename error codes |
93 |
case SNAPD_ERROR_FAILED: |
94 |
g_dbus_method_invocation_return_dbus_error (request->invocation, "io.snapcraft.SnapdLoginService.Error.Failed", error->message); |
|
95 |
break; |
|
116
by Robert Ancell
Fix errors not being correct on first use of snapd_login |
96 |
default: |
97 |
g_dbus_method_invocation_return_gerror (request->invocation, error); |
|
98 |
break; |
|
99 |
}
|
|
100 |
}
|
|
101 |
||
102 |
static void |
|
101
by Robert Ancell
Make a D-Bus service to allow non-root users to login |
103 |
login_result_cb (GObject *object, GAsyncResult *result, gpointer user_data) |
104 |
{
|
|
105 |
g_autoptr(LoginRequest) request = user_data; |
|
132
by Robert Ancell
Make snapd_client_login consistent with snapd_login (since we added snapd_login_service and this is not the normal case anymore) |
106 |
g_autoptr(SnapdAuthData) auth_data = NULL; |
101
by Robert Ancell
Make a D-Bus service to allow non-root users to login |
107 |
g_autoptr(GError) error = NULL; |
108 |
||
132
by Robert Ancell
Make snapd_client_login consistent with snapd_login (since we added snapd_login_service and this is not the normal case anymore) |
109 |
auth_data = snapd_client_login_finish (request->client, result, &error); |
110 |
if (auth_data == NULL) { |
|
116
by Robert Ancell
Fix errors not being correct on first use of snapd_login |
111 |
return_error (request, error); |
101
by Robert Ancell
Make a D-Bus service to allow non-root users to login |
112 |
return; |
113 |
}
|
|
114 |
||
115 |
io_snapcraft_snapd_login_service_complete_login (service, request->invocation, |
|
116 |
snapd_auth_data_get_macaroon (auth_data), |
|
117 |
(const gchar *const *) snapd_auth_data_get_discharges (auth_data)); |
|
118 |
}
|
|
119 |
||
120 |
static void |
|
121 |
auth_cb (GObject *object, GAsyncResult *result, gpointer user_data) |
|
122 |
{
|
|
123 |
g_autoptr(LoginRequest) request = user_data; |
|
124 |
g_autoptr(PolkitAuthorizationResult) r = NULL; |
|
125 |
g_autoptr(GError) error = NULL; |
|
126 |
||
127 |
r = polkit_authority_check_authorization_finish (authority, result, &error); |
|
128 |
if (r == NULL) { |
|
129 |
g_dbus_method_invocation_return_error (request->invocation, |
|
130 |
SNAPD_ERROR, |
|
131 |
SNAPD_ERROR_PERMISSION_DENIED, |
|
132 |
"Failed to get permission from Polkit: %s", error->message); |
|
133 |
return; |
|
134 |
}
|
|
135 |
||
136 |
if (!polkit_authorization_result_get_is_authorized (r)) { |
|
137 |
g_dbus_method_invocation_return_error (request->invocation, |
|
138 |
SNAPD_ERROR, |
|
139 |
SNAPD_ERROR_PERMISSION_DENIED, |
|
140 |
"Permission denied by Polkit"); |
|
141 |
return; |
|
142 |
}
|
|
143 |
||
144 |
g_debug ("Requesting login from snapd..."); |
|
145 |
||
146 |
request->client = snapd_client_new (); |
|
147 |
if (!snapd_client_connect_sync (request->client, NULL, &error)) { |
|
116
by Robert Ancell
Fix errors not being correct on first use of snapd_login |
148 |
return_error (request, error); |
101
by Robert Ancell
Make a D-Bus service to allow non-root users to login |
149 |
return; |
150 |
}
|
|
151 |
||
152 |
snapd_client_login_async (request->client, |
|
153 |
request->username, request->password, request->otp, NULL, |
|
154 |
login_result_cb, request); |
|
155 |
g_steal_pointer (&request); |
|
156 |
}
|
|
157 |
||
158 |
static gboolean |
|
159 |
login_cb (IoSnapcraftSnapdLoginService *service, |
|
160 |
GDBusMethodInvocation *invocation, |
|
161 |
const gchar *username, |
|
162 |
const gchar *password, |
|
163 |
const gchar *otp, |
|
164 |
gpointer user_data) |
|
165 |
{
|
|
166 |
g_autoptr(LoginRequest) request = NULL; |
|
167 |
g_autoptr(PolkitSubject) subject = NULL; |
|
168 |
||
169 |
request = g_slice_new0 (LoginRequest); |
|
170 |
request->invocation = g_object_ref (invocation); |
|
171 |
request->username = g_strdup (username); |
|
172 |
request->password = g_strdup (password); |
|
173 |
if (otp[0] != '\0') |
|
174 |
request->otp = g_strdup (otp); |
|
175 |
||
176 |
g_debug ("Processing login request..."); |
|
177 |
||
178 |
subject = polkit_system_bus_name_new (g_dbus_method_invocation_get_sender (invocation)); |
|
179 |
polkit_authority_check_authorization (authority, |
|
180 |
subject, "io.snapcraft.login", NULL, |
|
181 |
POLKIT_CHECK_AUTHORIZATION_FLAGS_ALLOW_USER_INTERACTION, |
|
182 |
NULL, |
|
183 |
auth_cb, g_steal_pointer (&request)); |
|
184 |
||
185 |
return TRUE; |
|
186 |
}
|
|
187 |
||
188 |
static void |
|
189 |
bus_acquired_cb (GDBusConnection *connection, |
|
190 |
const gchar *name, |
|
191 |
gpointer user_data) |
|
192 |
{
|
|
193 |
g_autoptr(GError) error = NULL; |
|
194 |
||
195 |
g_debug ("Connected to D-Bus"); |
|
196 |
||
197 |
service = io_snapcraft_snapd_login_service_skeleton_new (); |
|
198 |
g_signal_connect (service, "handle-login", G_CALLBACK (login_cb), NULL); |
|
199 |
if (!g_dbus_interface_skeleton_export (G_DBUS_INTERFACE_SKELETON (service), |
|
200 |
connection, |
|
201 |
"/io/snapcraft/SnapdLoginService", |
|
202 |
&error)) { |
|
203 |
g_warning ("Failed to register object: %s", error->message); |
|
204 |
result = EXIT_FAILURE; |
|
205 |
g_main_loop_quit (loop); |
|
206 |
}
|
|
207 |
}
|
|
208 |
||
209 |
static void |
|
210 |
name_acquired_cb (GDBusConnection *connection, |
|
211 |
const gchar *name, |
|
212 |
gpointer user_data) |
|
213 |
{
|
|
214 |
g_debug ("Acquired bus name %s", name); |
|
215 |
}
|
|
216 |
||
217 |
static void |
|
218 |
name_lost_cb (GDBusConnection *connection, |
|
219 |
const gchar *name, |
|
220 |
gpointer user_data) |
|
221 |
{
|
|
222 |
if (connection == NULL) { |
|
223 |
g_warning ("Failed to connect to bus"); |
|
224 |
result = EXIT_FAILURE; |
|
225 |
}
|
|
226 |
||
227 |
g_info ("Lost bus name %s", name); |
|
228 |
g_main_loop_quit (loop); |
|
229 |
}
|
|
230 |
||
231 |
||
232 |
int main (int argc, char **argv) |
|
233 |
{
|
|
234 |
g_autoptr(GError) error = NULL; |
|
235 |
||
236 |
loop = g_main_loop_new (NULL, FALSE); |
|
237 |
||
238 |
authority = polkit_authority_get_sync (NULL, &error); |
|
239 |
if (authority == NULL) { |
|
240 |
g_warning ("Failed to get Polkit authority: %s", error->message); |
|
241 |
return EXIT_FAILURE; |
|
242 |
}
|
|
243 |
g_debug ("Connected to Polkit"); |
|
244 |
||
245 |
g_bus_own_name (G_BUS_TYPE_SYSTEM, |
|
246 |
"io.snapcraft.SnapdLoginService", |
|
247 |
G_BUS_NAME_OWNER_FLAGS_ALLOW_REPLACEMENT | G_BUS_NAME_OWNER_FLAGS_REPLACE, |
|
248 |
bus_acquired_cb, |
|
249 |
name_acquired_cb, |
|
250 |
name_lost_cb, |
|
251 |
NULL, NULL); |
|
252 |
||
253 |
g_main_loop_run (loop); |
|
254 |
||
255 |
return result; |
|
256 |
}
|