]>
Commit | Line | Data |
---|---|---|
74a30a58 UD |
1 | /* SELinux access controls for nscd. |
2 | Copyright (C) 2004 Free Software Foundation, Inc. | |
3 | This file is part of the GNU C Library. | |
4 | Contributed by Matthew Rickard <mjricka@epoch.ncsc.mil>, 2004. | |
5 | ||
6 | The GNU C Library is free software; you can redistribute it and/or | |
7 | modify it under the terms of the GNU Lesser General Public | |
8 | License as published by the Free Software Foundation; either | |
9 | version 2.1 of the License, or (at your option) any later version. | |
10 | ||
11 | The GNU C Library is distributed in the hope that it will be useful, | |
12 | but WITHOUT ANY WARRANTY; without even the implied warranty of | |
13 | MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU | |
14 | Lesser General Public License for more details. | |
15 | ||
16 | You should have received a copy of the GNU Lesser General Public | |
17 | License along with the GNU C Library; if not, write to the Free | |
18 | Software Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA | |
19 | 02111-1307 USA. */ | |
20 | ||
21 | #include <error.h> | |
22 | #include <errno.h> | |
23 | #include <libintl.h> | |
24 | #include <pthread.h> | |
25 | #include <stdarg.h> | |
26 | #include <stdio.h> | |
27 | #include <stdlib.h> | |
28 | #include <syslog.h> | |
29 | #include <selinux/av_permissions.h> | |
30 | #include <selinux/avc.h> | |
31 | #include <selinux/flask.h> | |
32 | #include <selinux/selinux.h> | |
33 | ||
34 | #include "dbg_log.h" | |
35 | #include "selinux.h" | |
36 | ||
37 | ||
38 | #ifdef HAVE_SELINUX | |
39 | /* Global variable to tell if the kernel has SELinux support. */ | |
40 | int selinux_enabled; | |
41 | ||
42 | /* Define mappings of access vector permissions to request types. */ | |
43 | static const int perms[LASTREQ] = | |
44 | { | |
45 | [GETPWBYNAME] = NSCD__GETPWD, | |
46 | [GETPWBYUID] = NSCD__GETPWD, | |
47 | [GETGRBYNAME] = NSCD__GETGRP, | |
48 | [GETGRBYGID] = NSCD__GETGRP, | |
49 | [GETHOSTBYNAME] = NSCD__GETHOST, | |
50 | [GETHOSTBYNAMEv6] = NSCD__GETHOST, | |
51 | [GETHOSTBYADDR] = NSCD__GETHOST, | |
52 | [GETHOSTBYADDRv6] = NSCD__GETHOST, | |
53 | [GETSTAT] = NSCD__GETSTAT, | |
54 | [SHUTDOWN] = NSCD__ADMIN, | |
55 | [INVALIDATE] = NSCD__ADMIN, | |
56 | [GETFDPW] = NSCD__SHMEMPWD, | |
57 | [GETFDGR] = NSCD__SHMEMGRP, | |
58 | [GETFDHST] = NSCD__SHMEMHOST, | |
59 | [GETAI] = NSCD__GETHOST | |
60 | }; | |
61 | ||
62 | /* Store an entry ref to speed AVC decisions. */ | |
63 | static struct avc_entry_ref aeref; | |
64 | ||
65 | /* Thread to listen for SELinux status changes via netlink. */ | |
66 | static pthread_t avc_notify_thread; | |
67 | ||
68 | /* Prototypes for AVC callback functions. */ | |
69 | static void *avc_create_thread (void (*run) (void)); | |
70 | static void avc_stop_thread (void *thread); | |
71 | static void *avc_alloc_lock (void); | |
72 | static void avc_get_lock (void *lock); | |
73 | static void avc_release_lock (void *lock); | |
74 | static void avc_free_lock (void *lock); | |
75 | ||
76 | /* AVC callback structures for use in avc_init. */ | |
77 | static const struct avc_log_callback log_cb = | |
78 | { | |
79 | .func_log = dbg_log, | |
80 | .func_audit = NULL | |
81 | }; | |
82 | static const struct avc_thread_callback thread_cb = | |
83 | { | |
84 | .func_create_thread = avc_create_thread, | |
85 | .func_stop_thread = avc_stop_thread | |
86 | }; | |
87 | static const struct avc_lock_callback lock_cb = | |
88 | { | |
89 | .func_alloc_lock = avc_alloc_lock, | |
90 | .func_get_lock = avc_get_lock, | |
91 | .func_release_lock = avc_release_lock, | |
92 | .func_free_lock = avc_free_lock | |
93 | }; | |
94 | ||
95 | ||
96 | /* Determine if we are running on an SELinux kernel. Set selinux_enabled | |
97 | to the result. */ | |
98 | void | |
99 | nscd_selinux_enabled (int *selinux_enabled) | |
100 | { | |
101 | *selinux_enabled = is_selinux_enabled (); | |
102 | if (*selinux_enabled < 0) | |
103 | { | |
104 | dbg_log (_("Failed to determine if kernel supports SELinux")); | |
105 | exit (EXIT_FAILURE); | |
106 | } | |
107 | } | |
108 | ||
109 | ||
110 | /* Create thread for AVC netlink notification. */ | |
111 | static void * | |
112 | avc_create_thread (void (*run) (void)) | |
113 | { | |
114 | int rc; | |
115 | ||
116 | rc = | |
117 | pthread_create (&avc_notify_thread, NULL, (void *(*) (void *)) run, NULL); | |
118 | if (rc != 0) | |
119 | error (EXIT_FAILURE, rc, _("Failed to start AVC thread")); | |
120 | ||
121 | return &avc_notify_thread; | |
122 | } | |
123 | ||
124 | ||
125 | /* Stop AVC netlink thread. */ | |
126 | static void | |
127 | avc_stop_thread (void *thread) | |
128 | { | |
129 | pthread_cancel (*(pthread_t *) thread); | |
130 | } | |
131 | ||
132 | ||
133 | /* Allocate a new AVC lock. */ | |
134 | static void * | |
135 | avc_alloc_lock (void) | |
136 | { | |
137 | pthread_mutex_t *avc_mutex; | |
138 | ||
139 | avc_mutex = malloc (sizeof (pthread_mutex_t)); | |
140 | if (avc_mutex == NULL) | |
141 | error (EXIT_FAILURE, errno, _("Failed to create AVC lock")); | |
142 | pthread_mutex_init (avc_mutex, NULL); | |
143 | ||
144 | return avc_mutex; | |
145 | } | |
146 | ||
147 | ||
148 | /* Acquire an AVC lock. */ | |
149 | static void | |
150 | avc_get_lock (void *lock) | |
151 | { | |
152 | pthread_mutex_lock (lock); | |
153 | } | |
154 | ||
155 | ||
156 | /* Release an AVC lock. */ | |
157 | static void | |
158 | avc_release_lock (void *lock) | |
159 | { | |
160 | pthread_mutex_unlock (lock); | |
161 | } | |
162 | ||
163 | ||
164 | /* Free an AVC lock. */ | |
165 | static void | |
166 | avc_free_lock (void *lock) | |
167 | { | |
168 | pthread_mutex_destroy (lock); | |
169 | free (lock); | |
170 | } | |
171 | ||
172 | ||
173 | /* Initialize the user space access vector cache (AVC) for NSCD along with | |
174 | log/thread/lock callbacks. */ | |
175 | void | |
176 | nscd_avc_init (void) | |
177 | { | |
178 | avc_entry_ref_init (&aeref); | |
179 | ||
180 | if (avc_init ("avc", NULL, &log_cb, &thread_cb, &lock_cb) < 0) | |
181 | error (EXIT_FAILURE, errno, _("Failed to start AVC")); | |
182 | else | |
183 | dbg_log (_("Access Vector Cache (AVC) started")); | |
184 | } | |
185 | ||
186 | ||
187 | /* Check the permission from the caller (via getpeercon) to nscd. | |
188 | Returns 0 if access is allowed, 1 if denied, and -1 on error. */ | |
189 | int | |
190 | nscd_request_avc_has_perm (int fd, request_type req) | |
191 | { | |
192 | /* Initialize to NULL so we know what to free in case of failure. */ | |
193 | security_context_t scon = NULL; | |
194 | security_context_t tcon = NULL; | |
195 | security_id_t ssid = NULL; | |
196 | security_id_t tsid = NULL; | |
197 | int rc = -1; | |
198 | ||
199 | if (getpeercon (fd, &scon) < 0) | |
200 | { | |
201 | dbg_log (_("Error getting context of socket peer")); | |
202 | goto out; | |
203 | } | |
204 | if (getcon (&tcon) < 0) | |
205 | { | |
206 | dbg_log (_("Error getting context of nscd")); | |
207 | goto out; | |
208 | } | |
209 | if (avc_context_to_sid (scon, &ssid) < 0 || | |
210 | avc_context_to_sid (tcon, &tsid) < 0) | |
211 | { | |
212 | dbg_log (_("Error getting sid from context")); | |
213 | goto out; | |
214 | } | |
215 | ||
216 | rc = avc_has_perm (ssid, tsid, SECCLASS_NSCD, perms[req], &aeref, NULL) < 0; | |
217 | ||
218 | out: | |
219 | if (scon) | |
220 | freecon (scon); | |
221 | if (tcon) | |
222 | freecon (tcon); | |
223 | if (ssid) | |
224 | sidput (ssid); | |
225 | if (tsid) | |
226 | sidput (tsid); | |
227 | ||
228 | return rc; | |
229 | } | |
230 | ||
231 | ||
232 | /* Wrapper to get AVC statistics. */ | |
233 | void | |
234 | nscd_avc_cache_stats (struct avc_cache_stats *cstats) | |
235 | { | |
236 | avc_cache_stats (cstats); | |
237 | } | |
238 | ||
239 | ||
240 | /* Print the AVC statistics to stdout. */ | |
241 | void | |
242 | nscd_avc_print_stats (struct avc_cache_stats *cstats) | |
243 | { | |
244 | printf (_("\nSELinux AVC Statistics:\n\n" | |
245 | "%15u entry lookups\n" | |
246 | "%15u entry hits\n" | |
247 | "%15u entry misses\n" | |
248 | "%15u entry discards\n" | |
249 | "%15u CAV lookups\n" | |
250 | "%15u CAV hits\n" | |
251 | "%15u CAV probes\n" | |
252 | "%15u CAV misses\n"), | |
253 | cstats->entry_lookups, cstats->entry_hits, cstats->entry_misses, | |
254 | cstats->entry_discards, cstats->cav_lookups, cstats->cav_hits, | |
255 | cstats->cav_probes, cstats->cav_misses); | |
256 | } | |
257 | ||
258 | ||
259 | /* Clean up the AVC before exiting. */ | |
260 | void | |
261 | nscd_avc_destroy (void) | |
262 | { | |
263 | avc_destroy (); | |
264 | } | |
265 | ||
266 | #endif /* HAVE_SELINUX */ |