2004-08-23 Colin Walters <walters@redhat.com>
[platform/upstream/dbus.git] / bus / selinux.c
1 /* selinux.c  SELinux security checks for D-BUS
2  *
3  * Author: Matthew Rickard <mjricka@epoch.ncsc.mil>
4  *
5  * Licensed under the Academic Free License version 2.1
6  * 
7  * This program is free software; you can redistribute it and/or modify
8  * it under the terms of the GNU General Public License as published by
9  * the Free Software Foundation; either version 2 of the License, or
10  * (at your option) any later version.
11  *
12  * This program is distributed in the hope that it will be useful,
13  * but WITHOUT ANY WARRANTY; without even the implied warranty of
14  * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
15  * GNU General Public License for more details.
16  * 
17  * You should have received a copy of the GNU General Public License
18  * along with this program; if not, write to the Free Software
19  * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA  02111-1307  USA
20  *
21  */
22 #include <dbus/dbus-internals.h>
23 #include <dbus/dbus-string.h>
24 #include "selinux.h"
25 #include "services.h"
26 #include "policy.h"
27 #include "utils.h"
28 #include "config-parser.h"
29
30 #ifdef HAVE_SELINUX
31 #include <errno.h>
32 #include <pthread.h>
33 #include <syslog.h>
34 #include <selinux/selinux.h>
35 #include <selinux/avc.h>
36 #include <selinux/av_permissions.h>
37 #include <selinux/flask.h>
38 #include <signal.h>
39 #include <stdarg.h>
40 #endif /* HAVE_SELINUX */
41
42 #define BUS_SID_FROM_SELINUX(sid)  ((BusSELinuxID*) (sid))
43 #define SELINUX_SID_FROM_BUS(sid)  ((security_id_t) (sid))
44
45 #ifdef HAVE_SELINUX
46 /* Store the value telling us if SELinux is enabled in the kernel. */
47 static dbus_bool_t selinux_enabled = FALSE;
48
49 /* Store an avc_entry_ref to speed AVC decisions. */
50 static struct avc_entry_ref aeref;
51
52 /* Store the SID of the bus itself to use as the default. */
53 static security_id_t bus_sid = SECSID_WILD;
54
55 /* Thread to listen for SELinux status changes via netlink. */
56 static pthread_t avc_notify_thread;
57
58 /* Prototypes for AVC callback functions.  */
59 static void log_callback (const char *fmt, ...);
60 static void *avc_create_thread (void (*run) (void));
61 static void avc_stop_thread (void *thread);
62 static void *avc_alloc_lock (void);
63 static void avc_get_lock (void *lock);
64 static void avc_release_lock (void *lock);
65 static void avc_free_lock (void *lock);
66
67 /* AVC callback structures for use in avc_init.  */
68 static const struct avc_memory_callback mem_cb =
69 {
70   .func_malloc = dbus_malloc,
71   .func_free = dbus_free
72 };
73 static const struct avc_log_callback log_cb =
74 {
75   .func_log = log_callback,
76   .func_audit = NULL
77 };
78 static const struct avc_thread_callback thread_cb =
79 {
80   .func_create_thread = avc_create_thread,
81   .func_stop_thread = avc_stop_thread
82 };
83 static const struct avc_lock_callback lock_cb =
84 {
85   .func_alloc_lock = avc_alloc_lock,
86   .func_get_lock = avc_get_lock,
87   .func_release_lock = avc_release_lock,
88   .func_free_lock = avc_free_lock
89 };
90 #endif /* HAVE_SELINUX */
91
92 /**
93  * Log callback to log denial messages from the AVC.
94  * This is used in avc_init.  Logs to both standard
95  * error and syslogd.
96  *
97  * @param fmt the format string
98  * @param variable argument list
99  */
100 #ifdef HAVE_SELINUX
101 static void 
102 log_callback (const char *fmt, ...) 
103 {
104   va_list ap;
105   va_start(ap, fmt);
106   vsyslog (LOG_INFO, fmt, ap);
107   va_end(ap);
108 }
109
110 /**
111  * On a policy reload we need to reparse the SELinux configuration file, since
112  * this could have changed.  Send a SIGHUP to reload all configs.
113  */
114 static int
115 policy_reload_callback (u_int32_t event, security_id_t ssid, 
116                         security_id_t tsid, security_class_t tclass, 
117                         access_vector_t perms, access_vector_t *out_retained)
118 {
119   if (event == AVC_CALLBACK_RESET)
120     return raise (SIGHUP);
121   
122   return 0;
123 }
124
125 /**
126  * Create thread to notify the AVC of enforcing and policy reload
127  * changes via netlink.
128  *
129  * @param run the thread run function
130  * @return pointer to the thread
131  */
132 static void *
133 avc_create_thread (void (*run) (void))
134 {
135   int rc;
136
137   rc = pthread_create (&avc_notify_thread, NULL, (void *(*) (void *)) run, NULL);
138   if (rc != 0)
139     {
140       _dbus_warn ("Failed to start AVC thread: %s\n", _dbus_strerror (rc));
141       exit (1);
142     }
143   return &avc_notify_thread;
144 }
145
146 /* Stop AVC netlink thread.  */
147 static void
148 avc_stop_thread (void *thread)
149 {
150   pthread_cancel (*(pthread_t *) thread);
151 }
152
153 /* Allocate a new AVC lock.  */
154 static void *
155 avc_alloc_lock (void)
156 {
157   pthread_mutex_t *avc_mutex;
158
159   avc_mutex = dbus_new (pthread_mutex_t, 1);
160   if (avc_mutex == NULL)
161     {
162       _dbus_warn ("Could not create mutex: %s\n", _dbus_strerror (errno));
163       exit (1);
164     }
165   pthread_mutex_init (avc_mutex, NULL);
166
167   return avc_mutex;
168 }
169
170 /* Acquire an AVC lock.  */
171 static void
172 avc_get_lock (void *lock)
173 {
174   pthread_mutex_lock (lock);
175 }
176
177 /* Release an AVC lock.  */
178 static void
179 avc_release_lock (void *lock)
180 {
181   pthread_mutex_unlock (lock);
182 }
183
184 /* Free an AVC lock.  */
185 static void
186 avc_free_lock (void *lock)
187 {
188   pthread_mutex_destroy (lock);
189   dbus_free (lock);
190 }
191 #endif /* HAVE_SELINUX */
192
193 /**
194  * Initialize the user space access vector cache (AVC) for D-BUS and set up
195  * logging callbacks.
196  */
197 dbus_bool_t
198 bus_selinux_init (void)
199 {
200 #ifdef HAVE_SELINUX
201   int r;
202   char *bus_context;
203
204   _dbus_assert (bus_sid == SECSID_WILD);
205   
206   /* Determine if we are running an SELinux kernel. */
207   r = is_selinux_enabled ();
208   if (r < 0)
209     {
210       _dbus_warn ("Could not tell if SELinux is enabled: %s\n",
211                   _dbus_strerror (errno));
212       return FALSE;
213     }
214
215   selinux_enabled = r != 0;
216
217   if (!selinux_enabled)
218     {
219       _dbus_verbose ("SELinux not enabled in this kernel.\n");
220       return TRUE;
221     }
222
223   _dbus_verbose ("SELinux is enabled in this kernel.\n");
224
225   avc_entry_ref_init (&aeref);
226   if (avc_init ("avc", &mem_cb, &log_cb, &thread_cb, &lock_cb) < 0)
227     {
228       _dbus_warn ("Failed to start Access Vector Cache (AVC).\n");
229       return FALSE;
230     }
231   else
232     {
233       openlog ("dbus", LOG_PERROR, LOG_USER);
234       _dbus_verbose ("Access Vector Cache (AVC) started.\n");
235     }
236
237   if (avc_add_callback (policy_reload_callback, AVC_CALLBACK_RESET,
238                        NULL, NULL, 0, 0) < 0)
239     {
240       _dbus_warn ("Failed to add policy reload callback: %s\n",
241                   _dbus_strerror (errno));
242       avc_destroy ();
243       return FALSE;
244     }
245
246   bus_context = NULL;
247   bus_sid = SECSID_WILD;
248
249   if (getcon (&bus_context) < 0)
250     {
251       _dbus_verbose ("Error getting context of bus: %s\n",
252                      _dbus_strerror (errno));
253       return FALSE;
254     }
255       
256   if (avc_context_to_sid (bus_context, &bus_sid) < 0)
257     {
258       _dbus_verbose ("Error getting SID from bus context: %s\n",
259                      _dbus_strerror (errno));
260       freecon (bus_context);
261       return FALSE;
262     }
263
264   freecon (bus_context);
265   
266   return TRUE;
267 #else
268   return TRUE;
269 #endif /* HAVE_SELINUX */
270 }
271
272 /**
273  * Decrement SID reference count.
274  * 
275  * @param sid the SID to decrement
276  */
277 void
278 bus_selinux_id_unref (BusSELinuxID *sid)
279 {
280 #ifdef HAVE_SELINUX
281   if (!selinux_enabled)
282     return;
283
284   _dbus_assert (sid != NULL);
285   
286   sidput (SELINUX_SID_FROM_BUS (sid));
287 #endif /* HAVE_SELINUX */
288 }
289
290 void
291 bus_selinux_id_ref (BusSELinuxID *sid)
292 {
293 #ifdef HAVE_SELINUX
294   if (!selinux_enabled)
295     return;
296
297   _dbus_assert (sid != NULL);
298   
299   sidget (SELINUX_SID_FROM_BUS (sid));
300 #endif /* HAVE_SELINUX */
301 }
302
303 /**
304  * Determine if the SELinux security policy allows the given sender
305  * security context to go to the given recipient security context.
306  * This function determines if the requested permissions are to be
307  * granted from the connection to the message bus or to another
308  * optionally supplied security identifier (e.g. for a service
309  * context).  Currently these permissions are either send_msg or
310  * acquire_svc in the dbus class.
311  *
312  * @param sender_sid source security context
313  * @param override_sid is the target security context.  If SECSID_WILD this will
314  *        use the context of the bus itself (e.g. the default).
315  * @param target_class is the target security class.
316  * @param requested is the requested permissions.
317  * @returns #TRUE if security policy allows the send.
318  */
319 #ifdef HAVE_SELINUX
320 static dbus_bool_t
321 bus_selinux_check (BusSELinuxID        *sender_sid,
322                    BusSELinuxID        *override_sid,
323                    security_class_t     target_class,
324                    access_vector_t      requested)
325 {
326   if (!selinux_enabled)
327     return TRUE;
328
329   /* Make the security check.  AVC checks enforcing mode here as well. */
330   if (avc_has_perm (SELINUX_SID_FROM_BUS (sender_sid),
331                     override_sid ?
332                     SELINUX_SID_FROM_BUS (override_sid) :
333                     SELINUX_SID_FROM_BUS (bus_sid), 
334                     target_class, requested, &aeref, NULL) < 0)
335     {
336       _dbus_verbose ("SELinux denying due to security policy.\n");
337       return FALSE;
338     }
339   else
340     return TRUE;
341 }
342 #endif /* HAVE_SELINUX */
343
344 /**
345  * Returns true if the given connection can acquire a service,
346  * assuming the given security ID is needed for that service.
347  *
348  * @param connection connection that wants to own the service
349  * @param service_sid the SID of the service from the table
350  * @returns #TRUE if acquire is permitted.
351  */
352 dbus_bool_t
353 bus_selinux_allows_acquire_service (DBusConnection     *connection,
354                                     BusSELinuxID       *service_sid)
355 {
356 #ifdef HAVE_SELINUX
357   BusSELinuxID *connection_sid;
358   
359   if (!selinux_enabled)
360     return TRUE;
361
362   connection_sid = bus_connection_get_selinux_id (connection);
363   
364   return bus_selinux_check (connection_sid,
365                             service_sid,
366                             SECCLASS_DBUS,
367                             DBUS__ACQUIRE_SVC);
368 #else
369   return TRUE;
370 #endif /* HAVE_SELINUX */
371 }
372
373 /**
374  * Check if SELinux security controls allow the message to be sent to a
375  * particular connection based on the security context of the sender and
376  * that of the receiver. The destination connection need not be the
377  * addressed recipient, it could be an "eavesdropper"
378  *
379  * @param sender the sender of the message.
380  * @param proposed_recipient the connection the message is to be sent to.
381  * @returns whether to allow the send
382  */
383 dbus_bool_t
384 bus_selinux_allows_send (DBusConnection     *sender,
385                          DBusConnection     *proposed_recipient)
386 {
387 #ifdef HAVE_SELINUX
388   BusSELinuxID *recipient_sid;
389   BusSELinuxID *sender_sid;
390
391   if (!selinux_enabled)
392     return TRUE;
393
394   sender_sid = bus_connection_get_selinux_id (sender);
395   /* A NULL proposed_recipient means the bus itself. */
396   if (proposed_recipient)
397     recipient_sid = bus_connection_get_selinux_id (proposed_recipient);
398   else
399     recipient_sid = BUS_SID_FROM_SELINUX (bus_sid);
400
401   return bus_selinux_check (sender_sid, recipient_sid,
402                             SECCLASS_DBUS, DBUS__SEND_MSG);
403 #else
404   return TRUE;
405 #endif /* HAVE_SELINUX */
406 }
407
408 /**
409  * Gets the security context of a connection to the bus. It is up to
410  * the caller to freecon() when they are done. 
411  *
412  * @param connection the connection to get the context of.
413  * @param con the location to store the security context.
414  * @returns #TRUE if context is successfully obtained.
415  */
416 #ifdef HAVE_SELINUX
417 static dbus_bool_t
418 bus_connection_read_selinux_context (DBusConnection     *connection,
419                                      char              **con)
420 {
421   int fd;
422
423   if (!selinux_enabled)
424     return FALSE;
425
426   _dbus_assert (connection != NULL);
427   
428   if (!dbus_connection_get_unix_fd (connection, &fd))
429     {
430       _dbus_verbose ("Failed to get file descriptor of socket.\n");
431       return FALSE;
432     }
433   
434   if (getpeercon (fd, con) < 0)
435     {
436       _dbus_verbose ("Error getting context of socket peer: %s\n",
437                      _dbus_strerror (errno));
438       return FALSE;
439     }
440   
441   _dbus_verbose ("Successfully read connection context.\n");
442   return TRUE;
443 }
444 #endif /* HAVE_SELINUX */
445
446 /**
447  * Read the SELinux ID from the connection.
448  *
449  * @param connection the connection to read from
450  * @returns the SID if successfully determined, #NULL otherwise.
451  */
452 BusSELinuxID*
453 bus_selinux_init_connection_id (DBusConnection *connection,
454                                 DBusError      *error)
455 {
456 #ifdef HAVE_SELINUX
457   char *con;
458   security_id_t sid;
459   
460   if (!selinux_enabled)
461     return NULL;
462
463   if (!bus_connection_read_selinux_context (connection, &con))
464     {
465       dbus_set_error (error, DBUS_ERROR_FAILED,
466                       "Failed to read an SELinux context from connection");
467       _dbus_verbose ("Error getting peer context.\n");
468       return NULL;
469     }
470
471   _dbus_verbose ("Converting context to SID to store on connection\n");
472
473   if (avc_context_to_sid (con, &sid) < 0)
474     {
475       if (errno == ENOMEM)
476         BUS_SET_OOM (error);
477       else
478         dbus_set_error (error, DBUS_ERROR_FAILED,
479                         "Error getting SID from context: %s\n",
480                         _dbus_strerror (errno));
481       
482       _dbus_warn ("Error getting SID from context: %s\n",
483                   _dbus_strerror (errno));
484       
485       freecon (con);
486       return NULL;
487     }
488  
489   freecon (con); 
490   return BUS_SID_FROM_SELINUX (sid);
491 #else
492   return NULL;
493 #endif /* HAVE_SELINUX */
494 }
495
496
497 /**
498  * Function for freeing hash table data.  These SIDs
499  * should no longer be referenced.
500  */
501 static void
502 bus_selinux_id_table_free_value (BusSELinuxID *sid)
503 {
504 #ifdef HAVE_SELINUX
505   /* NULL sometimes due to how DBusHashTable works */
506   if (sid)
507     bus_selinux_id_unref (sid);
508 #endif /* HAVE_SELINUX */
509 }
510
511 /**
512  * Creates a new table mapping service names to security ID.
513  * A security ID is a "compiled" security context, a security
514  * context is just a string.
515  *
516  * @returns the new table or #NULL if no memory
517  */
518 DBusHashTable*
519 bus_selinux_id_table_new (void)
520 {
521   return _dbus_hash_table_new (DBUS_HASH_STRING,
522                                (DBusFreeFunction) dbus_free,
523                                (DBusFreeFunction) bus_selinux_id_table_free_value);
524 }
525
526 /** 
527  * Hashes a service name and service context into the service SID
528  * table as a string and a SID.
529  *
530  * @param service_name is the name of the service.
531  * @param service_context is the context of the service.
532  * @param service_table is the table to hash them into.
533  * @return #FALSE if not enough memory
534  */
535 dbus_bool_t
536 bus_selinux_id_table_insert (DBusHashTable *service_table,
537                              const char    *service_name,
538                              const char    *service_context)
539 {
540 #ifdef HAVE_SELINUX
541   dbus_bool_t retval;
542   security_id_t sid;
543   char *key;
544
545   if (!selinux_enabled)
546     return TRUE;
547
548   sid = SECSID_WILD;
549   retval = FALSE;
550
551   key = _dbus_strdup (service_name);
552   if (key == NULL)
553     return retval;
554   
555   if (avc_context_to_sid ((char *) service_context, &sid) < 0)
556     {
557       _dbus_assert (errno == ENOMEM);
558       goto out;
559     }
560
561   if (!_dbus_hash_table_insert_string (service_table,
562                                        key,
563                                        BUS_SID_FROM_SELINUX (sid)))
564     goto out;
565
566   _dbus_verbose ("Parsed \tservice: %s \n\t\tcontext: %s\n",
567                   key, 
568                   sid->ctx);
569
570   /* These are owned by the hash, so clear them to avoid unref */
571   key = NULL;
572   sid = SECSID_WILD;
573
574   retval = TRUE;
575   
576  out:
577   if (sid != SECSID_WILD)
578     sidput (sid);
579
580   if (key)
581     dbus_free (key);
582
583   return retval;
584 #else
585   return TRUE;
586 #endif /* HAVE_SELINUX */
587 }
588
589
590 /**
591  * Find the security identifier associated with a particular service
592  * name.  Return a pointer to this SID, or #NULL/SECSID_WILD if the
593  * service is not found in the hash table.  This should be nearly a
594  * constant time operation.  If SELinux support is not available,
595  * always return NULL.
596  *
597  * @param service_table the hash table to check for service name.
598  * @param service_name the name of the service to look for.
599  * @returns the SELinux ID associated with the service
600  */
601 BusSELinuxID*
602 bus_selinux_id_table_lookup (DBusHashTable    *service_table,
603                              const DBusString *service_name)
604 {
605 #ifdef HAVE_SELINUX
606   security_id_t sid;
607
608   sid = SECSID_WILD;     /* default context */
609
610   if (!selinux_enabled)
611     return NULL;
612   
613   _dbus_verbose ("Looking up service SID for %s\n",
614                  _dbus_string_get_const_data (service_name));
615
616   sid = _dbus_hash_table_lookup_string (service_table,
617                                         _dbus_string_get_const_data (service_name));
618
619   if (sid == SECSID_WILD)
620     _dbus_verbose ("Service %s not found\n", 
621                    _dbus_string_get_const_data (service_name));
622   else
623     _dbus_verbose ("Service %s found\n", 
624                    _dbus_string_get_const_data (service_name));
625
626   return BUS_SID_FROM_SELINUX (sid);
627 #endif /* HAVE_SELINUX */
628   return NULL;
629 }
630
631 /**
632  * Copy security ID table mapping from one table into another.
633  *
634  * @param dest the table to copy into
635  * @param override the table to copy from
636  * @returns #FALSE if out of memory
637  */
638 #ifdef HAVE_SELINUX
639 static dbus_bool_t
640 bus_selinux_id_table_copy_over (DBusHashTable    *dest,
641                                 DBusHashTable    *override)
642 {
643   const char *key;
644   char *key_copy;
645   BusSELinuxID *sid;
646   DBusHashIter iter;
647   
648   _dbus_hash_iter_init (override, &iter);
649   while (_dbus_hash_iter_next (&iter))
650     {
651       key = _dbus_hash_iter_get_string_key (&iter);
652       sid = _dbus_hash_iter_get_value (&iter);
653
654       key_copy = _dbus_strdup (key);
655       if (key_copy == NULL)
656         return FALSE;
657
658       if (!_dbus_hash_table_insert_string (dest,
659                                            key_copy,
660                                            sid))
661         {
662           dbus_free (key_copy);
663           return FALSE;
664         }
665
666       bus_selinux_id_ref (sid);
667     }
668
669   return TRUE;
670 }
671 #endif /* HAVE_SELINUX */
672
673 /**
674  * Creates the union of the two tables (each table maps a service
675  * name to a security ID). In case of the same service name in
676  * both tables, the security ID from "override" will be used.
677  *
678  * @param base the base table
679  * @param override the table that takes precedence in the merge
680  * @returns the new table, or #NULL if out of memory
681  */
682 DBusHashTable*
683 bus_selinux_id_table_union (DBusHashTable    *base,
684                             DBusHashTable    *override)
685 {
686   DBusHashTable *combined_table;
687
688   combined_table = bus_selinux_id_table_new ();
689
690   if (combined_table == NULL)
691     return NULL;
692   
693 #ifdef HAVE_SELINUX 
694   if (!selinux_enabled)
695     return combined_table;
696
697   if (!bus_selinux_id_table_copy_over (combined_table, base))
698     {
699       _dbus_hash_table_unref (combined_table);
700       return NULL;
701     }
702
703   if (!bus_selinux_id_table_copy_over (combined_table, override))
704     {
705       _dbus_hash_table_unref (combined_table);
706       return NULL;
707     }
708 #endif /* HAVE_SELINUX */
709   
710   return combined_table;
711 }
712
713 /**
714  * Get the SELinux policy root.  This is used to find the D-BUS
715  * specific config file within the policy.
716  */
717 const char *
718 bus_selinux_get_policy_root (void)
719 {
720 #ifdef HAVE_SELINUX
721   return selinux_policy_root ();
722 #else
723   return NULL;
724 #endif /* HAVE_SELINUX */
725
726
727 /**
728  * For debugging:  Print out the current hash table of service SIDs.
729  */
730 void
731 bus_selinux_id_table_print (DBusHashTable *service_table)
732 {
733 #ifdef DBUS_ENABLE_VERBOSE_MODE
734 #ifdef HAVE_SELINUX
735   DBusHashIter iter;
736
737   if (!selinux_enabled)
738     return;
739   
740   _dbus_verbose ("Service SID Table:\n");
741   _dbus_hash_iter_init (service_table, &iter);
742   while (_dbus_hash_iter_next (&iter))
743     {
744       const char *key = _dbus_hash_iter_get_string_key (&iter);
745       security_id_t sid = _dbus_hash_iter_get_value (&iter);
746       _dbus_verbose ("The key is %s\n", key);
747       _dbus_verbose ("The context is %s\n", sid->ctx);
748       _dbus_verbose ("The refcount is %d\n", sid->refcnt);
749     }
750 #endif /* HAVE_SELINUX */
751 #endif /* DBUS_ENABLE_VERBOSE_MODE */
752 }
753
754
755 #ifdef DBUS_ENABLE_VERBOSE_MODE
756 #ifdef HAVE_SELINUX
757 /**
758  * Print out some AVC statistics.
759  */
760 static void
761 bus_avc_print_stats (void)
762 {
763   struct avc_cache_stats cstats;
764
765   if (!selinux_enabled)
766     return;
767   
768   _dbus_verbose ("AVC Statistics:\n");
769   avc_cache_stats (&cstats);
770   avc_av_stats ();
771   _dbus_verbose ("AVC Cache Statistics:\n");
772   _dbus_verbose ("Entry lookups: %d\n", cstats.entry_lookups);
773   _dbus_verbose ("Entry hits: %d\n", cstats.entry_hits);
774   _dbus_verbose ("Entry misses %d\n", cstats.entry_misses);
775   _dbus_verbose ("Entry discards: %d\n", cstats.entry_discards);
776   _dbus_verbose ("CAV lookups: %d\n", cstats.cav_lookups);
777   _dbus_verbose ("CAV hits: %d\n", cstats.cav_hits);
778   _dbus_verbose ("CAV probes: %d\n", cstats.cav_probes);
779   _dbus_verbose ("CAV misses: %d\n", cstats.cav_misses);
780 }
781 #endif /* HAVE_SELINUX */
782 #endif /* DBUS_ENABLE_VERBOSE_MODE */
783
784
785 /**
786  * Destroy the AVC before we terminate.
787  */
788 void
789 bus_selinux_shutdown (void)
790 {
791 #ifdef HAVE_SELINUX
792   if (!selinux_enabled)
793     return;
794
795   sidput (bus_sid);
796   bus_sid = SECSID_WILD;
797   
798 #ifdef DBUS_ENABLE_VERBOSE_MODE
799   bus_avc_print_stats ();
800 #endif /* DBUS_ENABLE_VERBOSE_MODE */
801
802   avc_destroy ();
803 #endif /* HAVE_SELINUX */
804 }
805