1
From 6995d30816f0c76844dee4dc9022296a03258457 Mon Sep 17 00:00:00 2001
2
From: Vincent Untz <vuntz@suse.com>
3
Date: Wed, 03 Oct 2012 16:21:41 +0000
4
Subject: Fix a bunch of issues when getting/putting a file from cups
6
There was basically no check for permissions. Now, we temporarily change
7
our effective uid/gid to the one of the user to open the file for
8
writing (when getting) or reading (when putting). We then only use
9
operations that work on the file descriptor to avoid potential race
12
Before that, people could:
13
- overwrite any file with the content of a cups resource
14
- put any file in a cups resource
16
Part of fix for CVE-2012-4510.
18
diff --git a/src/cups-pk-helper-mechanism.c b/src/cups-pk-helper-mechanism.c
19
index d456499..a468a2a 100644
20
--- a/src/cups-pk-helper-mechanism.c
21
+++ b/src/cups-pk-helper-mechanism.c
22
@@ -559,14 +559,28 @@ cph_mechanism_file_get (CphIfaceMechanism *object,
25
CphMechanism *mechanism = CPH_MECHANISM (object);
26
+ unsigned int sender_uid;
29
_cph_mechanism_emit_called (mechanism);
31
+ if (!_cph_mechanism_get_sender_uid (mechanism, context, &sender_uid)) {
34
+ error = g_error_new (CPH_MECHANISM_ERROR,
35
+ CPH_MECHANISM_ERROR_GENERAL,
36
+ "Cannot determine sender UID");
37
+ g_dbus_method_invocation_return_gerror (context, error);
38
+ g_error_free (error);
43
if (!_check_polkit_for_action (mechanism, context, "server-settings"))
46
- ret = cph_cups_file_get (mechanism->priv->cups, resource, filename);
47
+ ret = cph_cups_file_get (mechanism->priv->cups,
48
+ resource, filename, sender_uid);
50
cph_iface_mechanism_complete_file_get (
52
@@ -581,14 +595,28 @@ cph_mechanism_file_put (CphIfaceMechanism *object,
55
CphMechanism *mechanism = CPH_MECHANISM (object);
56
+ unsigned int sender_uid;
59
_cph_mechanism_emit_called (mechanism);
61
+ if (!_cph_mechanism_get_sender_uid (mechanism, context, &sender_uid)) {
64
+ error = g_error_new (CPH_MECHANISM_ERROR,
65
+ CPH_MECHANISM_ERROR_GENERAL,
66
+ "Cannot determine sender UID");
67
+ g_dbus_method_invocation_return_gerror (context, error);
68
+ g_error_free (error);
73
if (!_check_polkit_for_action (mechanism, context, "server-settings"))
76
- ret = cph_cups_file_put (mechanism->priv->cups, resource, filename);
77
+ ret = cph_cups_file_put (mechanism->priv->cups,
78
+ resource, filename, sender_uid);
80
cph_iface_mechanism_complete_file_put (
82
diff --git a/src/cups.c b/src/cups.c
83
index 7d46c96..6ff6cba 100644
92
#include <sys/types.h>
94
@@ -510,6 +511,34 @@ _CPH_CUPS_IS_VALID (filename, "filename", TRUE, CPH_STR_MAXLEN)
96
******************************************************/
99
+_cph_cups_set_effective_id (unsigned int sender_uid)
101
+ struct passwd *password_entry;
103
+ password_entry = getpwuid ((uid_t) sender_uid);
105
+ if (password_entry == NULL ||
106
+ setegid (password_entry->pw_gid) != 0)
109
+ if (seteuid (sender_uid) != 0) {
110
+ if (getgid () != getegid ())
111
+ setegid (getgid ());
120
+_cph_cups_reset_effective_id (void)
122
+ seteuid (getuid ());
123
+ setegid (getgid ());
127
_cph_cups_add_printer_uri (ipp_t *request,
129
@@ -1081,14 +1110,15 @@ cph_cups_is_printer_local (CphCups *cups,
133
-cph_cups_file_get (CphCups *cups,
134
- const char *resource,
135
- const char *filename)
136
+cph_cups_file_get (CphCups *cups,
137
+ const char *resource,
138
+ const char *filename,
139
+ unsigned int sender_uid)
141
http_status_t status;
143
struct stat file_stat;
148
g_return_val_if_fail (CPH_IS_CUPS (cups), FALSE);
150
@@ -1097,44 +1127,83 @@ cph_cups_file_get (CphCups *cups,
151
if (!_cph_cups_is_filename_valid (cups, filename))
154
- stat (filename, &file_stat);
155
- uid = file_stat.st_uid;
156
- gid = file_stat.st_gid;
157
+ if (!_cph_cups_set_effective_id (sender_uid)) {
158
+ error = g_strdup_printf ("Cannot check if \"%s\" is "
160
+ filename, strerror (errno));
161
+ _cph_cups_set_internal_status (cups, error);
167
+ fd = open (filename, O_WRONLY | O_NOFOLLOW | O_TRUNC);
169
+ _cph_cups_reset_effective_id ();
172
+ error = g_strdup_printf ("Cannot open \"%s\": %s",
173
+ filename, strerror (errno));
174
+ _cph_cups_set_internal_status (cups, error);
181
+ if (fstat (fd, &file_stat) != 0) {
182
+ error = g_strdup_printf ("Cannot write to \"%s\": %s",
183
+ filename, strerror (errno));
184
+ _cph_cups_set_internal_status (cups, error);
192
+ if (!S_ISREG (file_stat.st_mode)) {
193
+ /* hrm, this looks suspicious... we won't help */
194
+ error = g_strdup_printf ("File \"%s\" is not a regular file.",
196
+ _cph_cups_set_internal_status (cups, error);
204
/* reset the internal status: we'll use the http status */
205
_cph_cups_set_internal_status (cups, NULL);
207
- status = cupsGetFile (cups->priv->connection, resource, filename);
208
+ status = cupsGetFd (cups->priv->connection, resource, fd);
210
/* FIXME: There's a bug where the cups connection can fail with EPIPE.
211
- * We're work-arounding it here until it's fixed in cups. */
212
+ * We're working around it here until it's fixed in cups. */
213
if (status != HTTP_OK) {
214
- if (cph_cups_reconnect (cups)) {
217
- /* if cupsGetFile fail, then filename is unlinked */
218
- fd = open (filename, O_CREAT, S_IRUSR | S_IWUSR);
220
- chown (filename, uid, gid);
222
- _cph_cups_set_internal_status (cups, NULL);
224
- status = cupsGetFile (cups->priv->connection,
225
- resource, filename);
227
+ if (cph_cups_reconnect (cups))
228
+ status = cupsGetFd (cups->priv->connection,
234
_cph_cups_set_internal_status_from_http (cups, status);
236
return (status == HTTP_OK);
240
-cph_cups_file_put (CphCups *cups,
241
- const char *resource,
242
- const char *filename)
243
+cph_cups_file_put (CphCups *cups,
244
+ const char *resource,
245
+ const char *filename,
246
+ unsigned int sender_uid)
248
http_status_t status;
250
+ struct stat file_stat;
253
g_return_val_if_fail (CPH_IS_CUPS (cups), FALSE);
255
@@ -1143,10 +1212,58 @@ cph_cups_file_put (CphCups *cups,
256
if (!_cph_cups_is_filename_valid (cups, filename))
259
+ if (!_cph_cups_set_effective_id (sender_uid)) {
260
+ error = g_strdup_printf ("Cannot check if \"%s\" is "
262
+ filename, strerror (errno));
263
+ _cph_cups_set_internal_status (cups, error);
269
+ fd = open (filename, O_RDONLY);
271
+ _cph_cups_reset_effective_id ();
274
+ error = g_strdup_printf ("Cannot open \"%s\": %s",
275
+ filename, strerror (errno));
276
+ _cph_cups_set_internal_status (cups, error);
282
+ if (fstat (fd, &file_stat) != 0) {
283
+ error = g_strdup_printf ("Cannot read \"%s\": %s",
284
+ filename, strerror (errno));
285
+ _cph_cups_set_internal_status (cups, error);
293
+ if (!S_ISREG (file_stat.st_mode)) {
294
+ /* hrm, this looks suspicious... we won't help */
295
+ error = g_strdup_printf ("File \"%s\" is not a regular file.",
297
+ _cph_cups_set_internal_status (cups, error);
305
/* reset the internal status: we'll use the http status */
306
_cph_cups_set_internal_status (cups, NULL);
308
- status = cupsPutFile (cups->priv->connection, resource, filename);
309
+ status = cupsPutFd (cups->priv->connection, resource, fd);
313
_cph_cups_set_internal_status_from_http (cups, status);
315
diff --git a/src/cups.h b/src/cups.h
316
index 2832533..3017792 100644
319
@@ -70,13 +70,15 @@ char *cph_cups_printer_get_uri (CphCups *cups,
320
gboolean cph_cups_is_printer_local (CphCups *cups,
321
const char *printer_name);
323
-gboolean cph_cups_file_get (CphCups *cups,
324
- const char *resource,
325
- const char *filename);
327
-gboolean cph_cups_file_put (CphCups *cups,
328
- const char *resource,
329
- const char *filename);
330
+gboolean cph_cups_file_get (CphCups *cups,
331
+ const char *resource,
332
+ const char *filename,
333
+ unsigned int sender_uid);
335
+gboolean cph_cups_file_put (CphCups *cups,
336
+ const char *resource,
337
+ const char *filename,
338
+ unsigned int sender_uid);
340
gboolean cph_cups_server_get_settings (CphCups *cups,
341
GVariant **settings);