summaryrefslogtreecommitdiffstats
path: root/meta-security/recipes-core/dbus/dbus-cynara/0003-Add-LSM-agnostic-support-for-LinuxSecurityLabel-cred.patch
blob: fcb85504d32d808ddd5d5b15f191c655f32853da (plain)
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
166
167
168
169
170
171
172
173
174
175
176
177
178
179
180
181
182
183
184
185
186
187
188
189
190
191
192
193
194
195
196
197
198
199
200
201
202
203
204
205
206
207
208
209
210
211
212
213
214
215
216
217
218
219
220
221
222
223
224
225
226
227
228
229
230
231
232
233
234
235
236
237
238
239
240
241
242
243
244
245
246
247
248
249
250
251
252
253
254
255
256
257
258
259
260
261
262
263
264
265
266
267
268
269
270
271
272
273
274
275
276
277
278
279
280
281
282
283
284
285
286
287
288
289
290
291
292
293
294
295
296
297
298
299
300
301
302
303
304
305
306
307
308
309
310
311
312
313
314
315
316
317
318
319
320
321
322
323
324
325
326
327
328
329
330
331
332
333
334
335
336
337
338
339
340
341
342
343
344
345
346
347
348
349
350
351
352
353
354
355
356
357
358
359
360
361
362
363
364
365
366
367
368
369
370
371
372
373
374
375
376
377
378
379
380
381
382
383
384
385
386
387
388
389
390
391
392
393
394
395
396
397
398
399
400
401
402
403
404
405
406
407
408
409
410
411
412
413
414
415
416
417
418
419
420
421
422
423
424
425
426
427
428
429
430
431
432
433
434
435
436
437
438
439
440
441
442
443
444
445
446
447
448
449
450
451
452
453
454
455
456
457
458
459
460
461
462
463
464
465
466
467
468
469
470
471
472
473
474
475
476
477
478
479
480
481
482
483
484
485
486
487
488
489
490
491
492
493
494
495
496
497
498
499
500
501
502
503
504
505
506
507
508
509
510
511
512
513
514
515
From 9da49d4eb6982c659fec988231baef8cd1b05be2 Mon Sep 17 00:00:00 2001
From: Simon McVittie <simon.mcvittie@collabora.co.uk>
Date: Wed, 11 Feb 2015 13:19:15 +0000
Subject: [PATCH 3/8] Add LSM-agnostic support for LinuxSecurityLabel
 credential

Bug: https://bugs.freedesktop.org/show_bug.cgi?id=89041
Change-Id: I70512843d1a7661c87461b1b6d86fbfbda934ad5
Reviewed-by: Philip Withnall <philip.withnall@collabora.co.uk>
Acked-by: Stephen Smalley <sds@tycho.nsa.gov> (for SELinux)
Acked-by: John Johansen <john.johansen@canonical.com> (for AppArmor)
Acked-by: Casey Schaufler <casey@schaufler-ca.com> (for Smack)
Tested-by: Tyler Hicks <tyhicks@canonical.com>
---
 bus/driver.c                    |  19 ++++++++
 dbus/dbus-auth.c                |  11 +++--
 dbus/dbus-connection-internal.h |   3 ++
 dbus/dbus-connection.c          |  26 ++++++++++
 dbus/dbus-credentials.c         |  68 ++++++++++++++++++++++++++
 dbus/dbus-credentials.h         |   4 ++
 dbus/dbus-sysdeps-unix.c        | 105 ++++++++++++++++++++++++++++++++++++++++
 dbus/dbus-transport.c           |  27 +++++++++++
 dbus/dbus-transport.h           |   3 ++
 9 files changed, 262 insertions(+), 4 deletions(-)

diff --git a/bus/driver.c b/bus/driver.c
index 888c7ca..11706f8 100644
--- a/bus/driver.c
+++ b/bus/driver.c
@@ -34,6 +34,7 @@
 #include "utils.h"
 
 #include <dbus/dbus-asv-util.h>
+#include <dbus/dbus-connection-internal.h>
 #include <dbus/dbus-string.h>
 #include <dbus/dbus-internals.h>
 #include <dbus/dbus-message.h>
@@ -1567,6 +1568,7 @@ bus_driver_handle_get_connection_credentials (DBusConnection *connection,
   DBusMessageIter reply_iter;
   DBusMessageIter array_iter;
   unsigned long ulong_val;
+  char *s;
   const char *service;
 
   _DBUS_ASSERT_ERROR_IS_CLEAR (error);
@@ -1601,6 +1603,23 @@ bus_driver_handle_get_connection_credentials (DBusConnection *connection,
         goto oom;
     }
 
+  if (_dbus_connection_get_linux_security_label (conn, &s))
+    {
+      if (s == NULL)
+        goto oom;
+
+      /* use the GVariant bytestring convention for strings of unknown
+       * encoding: include the \0 in the payload, for zero-copy reading */
+      if (!_dbus_asv_add_byte_array (&array_iter, "LinuxSecurityLabel",
+                                     s, strlen (s) + 1))
+        {
+          dbus_free (s);
+          goto oom;
+        }
+
+      dbus_free (s);
+    }
+
   if (!_dbus_asv_close (&reply_iter, &array_iter))
     goto oom;
 
diff --git a/dbus/dbus-auth.c b/dbus/dbus-auth.c
index 6a07665..aee877d 100644
--- a/dbus/dbus-auth.c
+++ b/dbus/dbus-auth.c
@@ -1102,20 +1102,23 @@ handle_server_data_external_mech (DBusAuth         *auth,
                                               auth->desired_identity))
         return FALSE;
 
-      /* also copy process ID from the socket credentials
+      /* also copy misc process info from the socket credentials
        */
       if (!_dbus_credentials_add_credential (auth->authorized_identity,
                                              DBUS_CREDENTIAL_UNIX_PROCESS_ID,
                                              auth->credentials))
         return FALSE;
 
-      /* also copy audit data from the socket credentials
-       */
       if (!_dbus_credentials_add_credential (auth->authorized_identity,
                                              DBUS_CREDENTIAL_ADT_AUDIT_DATA_ID,
                                              auth->credentials))
         return FALSE;
-      
+
+      if (!_dbus_credentials_add_credential (auth->authorized_identity,
+                                             DBUS_CREDENTIAL_LINUX_SECURITY_LABEL,
+                                             auth->credentials))
+        return FALSE;
+
       if (!send_ok (auth))
         return FALSE;
 
diff --git a/dbus/dbus-connection-internal.h b/dbus/dbus-connection-internal.h
index 2897404..64ef336 100644
--- a/dbus/dbus-connection-internal.h
+++ b/dbus/dbus-connection-internal.h
@@ -107,6 +107,9 @@ void              _dbus_connection_set_pending_fds_function       (DBusConnectio
                                                                    DBusPendingFdsChangeFunction callback,
                                                                    void *data);
 
+dbus_bool_t       _dbus_connection_get_linux_security_label       (DBusConnection  *connection,
+                                                                   char           **label_p);
+
 /* if DBUS_ENABLE_STATS */
 void _dbus_connection_get_stats (DBusConnection *connection,
                                  dbus_uint32_t  *in_messages,
diff --git a/dbus/dbus-connection.c b/dbus/dbus-connection.c
index b574207..8952b75 100644
--- a/dbus/dbus-connection.c
+++ b/dbus/dbus-connection.c
@@ -5322,6 +5322,32 @@ dbus_connection_set_unix_user_function (DBusConnection             *connection,
     (* old_free_function) (old_data);
 }
 
+/* Same calling convention as dbus_connection_get_windows_user */
+dbus_bool_t
+_dbus_connection_get_linux_security_label (DBusConnection  *connection,
+                                           char           **label_p)
+{
+  dbus_bool_t result;
+
+  _dbus_assert (connection != NULL);
+  _dbus_assert (label_p != NULL);
+
+  CONNECTION_LOCK (connection);
+
+  if (!_dbus_transport_try_to_authenticate (connection->transport))
+    result = FALSE;
+  else
+    result = _dbus_transport_get_linux_security_label (connection->transport,
+                                                       label_p);
+#ifndef __linux__
+  _dbus_assert (!result);
+#endif
+
+  CONNECTION_UNLOCK (connection);
+
+  return result;
+}
+
 /**
  * Gets the Windows user SID of the connection if known.  Returns
  * #TRUE if the ID is filled in.  Always returns #FALSE on non-Windows
diff --git a/dbus/dbus-credentials.c b/dbus/dbus-credentials.c
index 7325125..151bb00 100644
--- a/dbus/dbus-credentials.c
+++ b/dbus/dbus-credentials.c
@@ -50,6 +50,7 @@ struct DBusCredentials {
   dbus_uid_t unix_uid;
   dbus_pid_t pid;
   char *windows_sid;
+  char *linux_security_label;
   void *adt_audit_data;
   dbus_int32_t adt_audit_data_size;
 };
@@ -79,6 +80,7 @@ _dbus_credentials_new (void)
   creds->unix_uid = DBUS_UID_UNSET;
   creds->pid = DBUS_PID_UNSET;
   creds->windows_sid = NULL;
+  creds->linux_security_label = NULL;
   creds->adt_audit_data = NULL;
   creds->adt_audit_data_size = 0;
 
@@ -133,6 +135,7 @@ _dbus_credentials_unref (DBusCredentials    *credentials)
   if (credentials->refcount == 0)
     {
       dbus_free (credentials->windows_sid);
+      dbus_free (credentials->linux_security_label);
       dbus_free (credentials->adt_audit_data);
       dbus_free (credentials);
     }
@@ -193,6 +196,30 @@ _dbus_credentials_add_windows_sid (DBusCredentials    *credentials,
 }
 
 /**
+ * Add a Linux security label, as used by LSMs such as SELinux, Smack and
+ * AppArmor, to the credentials.
+ *
+ * @param credentials the object
+ * @param label the label
+ * @returns #FALSE if no memory
+ */
+dbus_bool_t
+_dbus_credentials_add_linux_security_label (DBusCredentials    *credentials,
+                                            const char         *label)
+{
+  char *copy;
+
+  copy = _dbus_strdup (label);
+  if (copy == NULL)
+    return FALSE;
+
+  dbus_free (credentials->linux_security_label);
+  credentials->linux_security_label = copy;
+
+  return TRUE;
+}
+
+/**
  * Add ADT audit data to the credentials.
  *
  * @param credentials the object
@@ -236,6 +263,8 @@ _dbus_credentials_include (DBusCredentials    *credentials,
       return credentials->unix_uid != DBUS_UID_UNSET;
     case DBUS_CREDENTIAL_WINDOWS_SID:
       return credentials->windows_sid != NULL;
+    case DBUS_CREDENTIAL_LINUX_SECURITY_LABEL:
+      return credentials->linux_security_label != NULL;
     case DBUS_CREDENTIAL_ADT_AUDIT_DATA_ID:
       return credentials->adt_audit_data != NULL;
     }
@@ -284,6 +313,19 @@ _dbus_credentials_get_windows_sid (DBusCredentials    *credentials)
 }
 
 /**
+ * Gets the Linux security label (as used by LSMs) from the credentials,
+ * or #NULL if the credentials object doesn't contain a security label.
+ *
+ * @param credentials the object
+ * @returns the security label
+ */
+const char *
+_dbus_credentials_get_linux_security_label (DBusCredentials *credentials)
+{
+  return credentials->linux_security_label;
+}
+
+/**
  * Gets the ADT audit data in the credentials, or #NULL if
  * the credentials object doesn't contain ADT audit data.
  *
@@ -329,6 +371,10 @@ _dbus_credentials_are_superset (DBusCredentials    *credentials,
     (possible_subset->windows_sid == NULL ||
      (credentials->windows_sid && strcmp (possible_subset->windows_sid,
                                           credentials->windows_sid) == 0)) &&
+    (possible_subset->linux_security_label == NULL ||
+     (credentials->linux_security_label != NULL &&
+      strcmp (possible_subset->linux_security_label,
+              credentials->linux_security_label) == 0)) &&
     (possible_subset->adt_audit_data == NULL ||
      (credentials->adt_audit_data && memcmp (possible_subset->adt_audit_data,
                                              credentials->adt_audit_data,
@@ -348,6 +394,7 @@ _dbus_credentials_are_empty (DBusCredentials    *credentials)
     credentials->pid == DBUS_PID_UNSET &&
     credentials->unix_uid == DBUS_UID_UNSET &&
     credentials->windows_sid == NULL &&
+    credentials->linux_security_label == NULL &&
     credentials->adt_audit_data == NULL;
 }
 
@@ -388,6 +435,9 @@ _dbus_credentials_add_credentials (DBusCredentials    *credentials,
                                       DBUS_CREDENTIAL_ADT_AUDIT_DATA_ID,
                                       other_credentials) &&
     _dbus_credentials_add_credential (credentials,
+                                      DBUS_CREDENTIAL_LINUX_SECURITY_LABEL,
+                                      other_credentials) &&
+    _dbus_credentials_add_credential (credentials,
                                       DBUS_CREDENTIAL_WINDOWS_SID,
                                       other_credentials);
 }
@@ -427,6 +477,13 @@ _dbus_credentials_add_credential (DBusCredentials    *credentials,
       if (!_dbus_credentials_add_windows_sid (credentials, other_credentials->windows_sid))
         return FALSE;
     } 
+  else if (which == DBUS_CREDENTIAL_LINUX_SECURITY_LABEL &&
+           other_credentials->linux_security_label != NULL)
+    {
+      if (!_dbus_credentials_add_linux_security_label (credentials,
+            other_credentials->linux_security_label))
+        return FALSE;
+    }
   else if (which == DBUS_CREDENTIAL_ADT_AUDIT_DATA_ID &&
            other_credentials->adt_audit_data != NULL) 
     {
@@ -449,6 +506,8 @@ _dbus_credentials_clear (DBusCredentials    *credentials)
   credentials->unix_uid = DBUS_UID_UNSET;
   dbus_free (credentials->windows_sid);
   credentials->windows_sid = NULL;
+  dbus_free (credentials->linux_security_label);
+  credentials->linux_security_label = NULL;
   dbus_free (credentials->adt_audit_data);
   credentials->adt_audit_data = NULL;
   credentials->adt_audit_data_size = 0;
@@ -540,6 +599,15 @@ _dbus_credentials_to_string_append (DBusCredentials    *credentials,
   else
     join = FALSE;
 
+  if (credentials->linux_security_label != NULL)
+    {
+      if (!_dbus_string_append_printf (string, "%slsm='%s'",
+                                       join ? " " : "",
+                                       credentials->linux_security_label))
+        goto oom;
+      join = TRUE;
+    }
+
   return TRUE;
 oom:
   return FALSE;
diff --git a/dbus/dbus-credentials.h b/dbus/dbus-credentials.h
index abcc4bb..ab74eac 100644
--- a/dbus/dbus-credentials.h
+++ b/dbus/dbus-credentials.h
@@ -34,6 +34,7 @@ typedef enum {
   DBUS_CREDENTIAL_UNIX_PROCESS_ID,
   DBUS_CREDENTIAL_UNIX_USER_ID,
   DBUS_CREDENTIAL_ADT_AUDIT_DATA_ID,
+  DBUS_CREDENTIAL_LINUX_SECURITY_LABEL,
   DBUS_CREDENTIAL_WINDOWS_SID
 } DBusCredentialType;
 
@@ -47,6 +48,8 @@ dbus_bool_t      _dbus_credentials_add_unix_uid             (DBusCredentials
                                                              dbus_uid_t          uid);
 dbus_bool_t      _dbus_credentials_add_windows_sid          (DBusCredentials    *credentials,
                                                              const char         *windows_sid);
+dbus_bool_t      _dbus_credentials_add_linux_security_label (DBusCredentials    *credentials,
+                                                             const char         *label);
 dbus_bool_t      _dbus_credentials_add_adt_audit_data       (DBusCredentials    *credentials,
                                                              void               *audit_data,
                                                              dbus_int32_t        size);
@@ -55,6 +58,7 @@ dbus_bool_t      _dbus_credentials_include                  (DBusCredentials
 dbus_pid_t       _dbus_credentials_get_pid                  (DBusCredentials    *credentials);
 dbus_uid_t       _dbus_credentials_get_unix_uid             (DBusCredentials    *credentials);
 const char*      _dbus_credentials_get_windows_sid          (DBusCredentials    *credentials);
+const char *     _dbus_credentials_get_linux_security_label (DBusCredentials    *credentials);
 void *           _dbus_credentials_get_adt_audit_data       (DBusCredentials    *credentials);
 dbus_int32_t     _dbus_credentials_get_adt_audit_data_size  (DBusCredentials    *credentials);
 dbus_bool_t      _dbus_credentials_are_superset             (DBusCredentials    *credentials,
diff --git a/dbus/dbus-sysdeps-unix.c b/dbus/dbus-sysdeps-unix.c
index fe891ab..61af423 100644
--- a/dbus/dbus-sysdeps-unix.c
+++ b/dbus/dbus-sysdeps-unix.c
@@ -1639,6 +1639,105 @@ write_credentials_byte (int             server_fd,
     }
 }
 
+/* return FALSE on OOM, TRUE otherwise, even if no credentials were found */
+static dbus_bool_t
+add_linux_security_label_to_credentials (int              client_fd,
+                                         DBusCredentials *credentials)
+{
+#if defined(__linux__) && defined(SO_PEERSEC)
+  DBusString buf;
+  socklen_t len = 1024;
+  dbus_bool_t oom = FALSE;
+
+  if (!_dbus_string_init_preallocated (&buf, len) ||
+      !_dbus_string_set_length (&buf, len))
+    return FALSE;
+
+  while (getsockopt (client_fd, SOL_SOCKET, SO_PEERSEC,
+         _dbus_string_get_data (&buf), &len) < 0)
+    {
+      int e = errno;
+
+      _dbus_verbose ("getsockopt failed with %s, len now %lu\n",
+                     _dbus_strerror (e), (unsigned long) len);
+
+      if (e != ERANGE || len <= _dbus_string_get_length (&buf))
+        {
+          _dbus_verbose ("Failed to getsockopt(SO_PEERSEC): %s\n",
+                         _dbus_strerror (e));
+          goto out;
+        }
+
+      /* If not enough space, len is updated to be enough.
+       * Try again with a large enough buffer. */
+      if (!_dbus_string_set_length (&buf, len))
+        {
+          oom = TRUE;
+          goto out;
+        }
+
+      _dbus_verbose ("will try again with %lu\n", (unsigned long) len);
+    }
+
+  if (len <= 0)
+    {
+      _dbus_verbose ("getsockopt(SO_PEERSEC) yielded <= 0 bytes: %lu\n",
+                     (unsigned long) len);
+      goto out;
+    }
+
+  if (len > _dbus_string_get_length (&buf))
+    {
+      _dbus_verbose ("%lu > %d", (unsigned long) len,
+                     _dbus_string_get_length (&buf));
+      _dbus_assert_not_reached ("getsockopt(SO_PEERSEC) overflowed");
+    }
+
+  if (_dbus_string_get_byte (&buf, len - 1) == 0)
+    {
+      /* the kernel included the trailing \0 in its count,
+       * but DBusString always has an extra \0 after the data anyway */
+      _dbus_verbose ("subtracting trailing \\0\n");
+      len--;
+    }
+
+  if (!_dbus_string_set_length (&buf, len))
+    {
+      _dbus_assert_not_reached ("shortening string should not lead to OOM");
+      oom = TRUE;
+      goto out;
+    }
+
+  if (strlen (_dbus_string_get_const_data (&buf)) != len)
+    {
+      /* LSM people on the linux-security-module@ mailing list say this
+       * should never happen: the label should be a bytestring with
+       * an optional trailing \0 */
+      _dbus_verbose ("security label from kernel had an embedded \\0, "
+                     "ignoring it\n");
+      goto out;
+    }
+
+  _dbus_verbose ("getsockopt(SO_PEERSEC): %lu bytes excluding \\0: %s\n",
+                 (unsigned long) len,
+                 _dbus_string_get_const_data (&buf));
+
+  if (!_dbus_credentials_add_linux_security_label (credentials,
+        _dbus_string_get_const_data (&buf)))
+    {
+      oom = TRUE;
+      goto out;
+    }
+
+out:
+  _dbus_string_free (&buf);
+  return !oom;
+#else
+  /* no error */
+  return TRUE;
+#endif
+}
+
 /**
  * Reads a single byte which must be nul (an error occurs otherwise),
  * and reads unix credentials if available. Clears the credentials
@@ -1922,6 +2021,12 @@ _dbus_read_credentials_socket  (int              client_fd,
         }
     }
 
+  if (!add_linux_security_label_to_credentials (client_fd, credentials))
+    {
+      _DBUS_SET_OOM (error);
+      return FALSE;
+    }
+
   return TRUE;
 }
 
diff --git a/dbus/dbus-transport.c b/dbus/dbus-transport.c
index e9dcc56..a43e7bb 100644
--- a/dbus/dbus-transport.c
+++ b/dbus/dbus-transport.c
@@ -1425,6 +1425,33 @@ _dbus_transport_set_unix_user_function (DBusTransport             *transport,
   transport->free_unix_user_data = free_data_function;
 }
 
+dbus_bool_t
+_dbus_transport_get_linux_security_label (DBusTransport  *transport,
+                                          char          **label_p)
+{
+  DBusCredentials *auth_identity;
+
+  *label_p = NULL;
+
+  if (!transport->authenticated)
+    return FALSE;
+
+  auth_identity = _dbus_auth_get_identity (transport->auth);
+
+  if (_dbus_credentials_include (auth_identity,
+                                 DBUS_CREDENTIAL_LINUX_SECURITY_LABEL))
+    {
+      /* If no memory, we are supposed to return TRUE and set NULL */
+      *label_p = _dbus_strdup (_dbus_credentials_get_linux_security_label (auth_identity));
+
+      return TRUE;
+    }
+  else
+    {
+      return FALSE;
+    }
+}
+
 /**
  * See dbus_connection_get_windows_user().
  *
diff --git a/dbus/dbus-transport.h b/dbus/dbus-transport.h
index 39c74c4..843f231 100644
--- a/dbus/dbus-transport.h
+++ b/dbus/dbus-transport.h
@@ -87,6 +87,9 @@ void               _dbus_transport_set_unix_user_function (DBusTransport
                                                            DBusFreeFunction           *old_free_data_function);
 dbus_bool_t        _dbus_transport_get_windows_user       (DBusTransport              *transport,
                                                            char                      **windows_sid_p);
+dbus_bool_t        _dbus_transport_get_linux_security_label (DBusTransport            *transport,
+                                                           char                      **label_p);
+
 void               _dbus_transport_set_windows_user_function (DBusTransport              *transport,
                                                               DBusAllowWindowsUserFunction   function,
                                                               void                       *data,
-- 
2.1.4