~azzar1/snapd-glib-fork/glib-2-40

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
}