The FreeRADIUS server $Id: 15bac2a4c627c01d1aa2047687b3418955ac7f00 $
Loading...
Searching...
No Matches
radlock.c
Go to the documentation of this file.
1/*
2 * This program is free software; you can redistribute it and/or modify
3 * it under the terms of the GNU General Public License as published by
4 * the Free Software Foundation; either version 2 of the License, or
5 * (at your option) any later version.
6 *
7 * This program is distributed in the hope that it will be useful,
8 * but WITHOUT ANY WARRANTY; without even the implied warranty of
9 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
10 * GNU General Public License for more details.
11 *
12 * You should have received a copy of the GNU General Public License
13 * along with this program; if not, write to the Free Software
14 * Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301, USA
15 */
16
17/**
18 * $Id: a34f01ab7d1045baa8c09d7e8d9fb2809ecd1e5b $
19 *
20 * @file radlock.c
21 * @brief Utility to examine semaphores used to provide exclusive running rights for a process
22 *
23 * @copyright 2021 Arran Cudbard-Bell (a.cudbardb@freeradius.org)
24 */
25RCSID("$Id: a34f01ab7d1045baa8c09d7e8d9fb2809ecd1e5b $")
26
27#include <freeradius-devel/autoconf.h>
28#include <freeradius-devel/util/perm.h>
29#include <freeradius-devel/util/sem.h>
30#include <freeradius-devel/util/value.h>
31#include <freeradius-devel/util/syserror.h>
32#include <freeradius-devel/util/strerror.h>
33
34#include <ctype.h>
35#include <stdarg.h>
36#include <stdbool.h>
37#include <sys/ipc.h>
38#include <sys/sem.h>
39
40#ifdef HAVE_GETOPT_H
41# include <getopt.h>
42#endif
43#include <assert.h>
44
45DIAG_OFF(unused-macros)
46#define INFO(fmt, ...) fprintf(stdout, fmt "\n", ## __VA_ARGS__)
47DIAG_ON(unused-macros)
48
49typedef enum {
51 RADLOCK_LOCK, //!< Acquire the semaphore if it's at 0.
52 RADLOCK_TRYLOCK, //!< Try and lock the semaphore and return if we can't.
53 RADLOCK_UNLOCK, //!< Unlock the semaphore.
54 RADLOCK_REMOVE, //!< Remove the semaphore.
55 RADLOCK_INFO, //!< Information about the semaphore.
56 RADLOCK_PERM //!< Modify permissions for a given semaphore.
58
60 { L("info"), RADLOCK_INFO },
61 { L("lock"), RADLOCK_LOCK },
62 { L("perm"), RADLOCK_PERM },
63 { L("remove"), RADLOCK_REMOVE },
64 { L("trylock"), RADLOCK_TRYLOCK },
65 { L("unlock"), RADLOCK_UNLOCK }
66};
68
69static NEVER_RETURNS void usage(int ret)
70{
71 fprintf(stderr, "usage: radlock <file> [lock|trylock|unlock|remove|info|perm]\n");
72 fprintf(stderr, " -u <uid> Desired user.\n");
73 fprintf(stderr, " -g <gid> Desired group.\n");
74 fprintf(stderr, " -m <perm> Octal permissions string.\n");
75 fprintf(stderr, " -h This help text.\n");
76 fprintf(stderr, "\n");
77 fprintf(stderr, "Simple utility to query the locking state of a config file\n");
78 fr_exit_now(ret);
79}
80
81#define EXIT_WITH_FAILURE exit(EXIT_FAILURE)
82#define EXIT_WITH_SUCCESS exit(EXIT_SUCCESS)
83
84/**
85 *
86 * @hidecallgraph
87 */
88int main(int argc, char *argv[])
89{
90 char c;
92 char const *file;
93 uid_t uid = geteuid();
94 bool uid_set = false;
95 gid_t gid = getegid();
96 bool gid_set = false;
97 long mode = S_IRUSR | S_IWUSR | S_IRGRP | S_IWGRP | S_IROTH;
98 bool mode_set = false;
99 int sem_id;
100
101 TALLOC_CTX *autofree;
102
104
105#ifndef NDEBUG
106 if (fr_fault_setup(autofree, getenv("PANIC_ACTION"), argv[0]) < 0) {
107 fr_perror("radict");
108 fr_exit(EXIT_FAILURE);
109 }
110#endif
111
112 talloc_set_log_stderr();
113
114 while ((c = getopt(argc, argv, "u:g:m:h")) != -1) switch (c) {
115 case 'u':
116 if (fr_perm_uid_from_str(autofree, &uid, optarg) < 0) {
117 fr_perror("radlock");
119 }
120 uid_set = true;
121 break;
122
123 case 'g':
124 if (fr_perm_uid_from_str(autofree, &gid, optarg) < 0) {
125 fr_perror("radlock");
127 }
128 gid_set = true;
129 break;
130
131 case 'm':
132 mode = strtol(optarg, NULL, 0); /* 0 base plus 0 prefix = octal */
133 if (errno == EINVAL) {
134 fr_perror("radlock - Bad mode value");
136 }
137 mode_set = true;
138 break;
139
140 case 'h':
141 default:
142 usage(EXIT_SUCCESS);
143 }
144 argc -= optind;
145 argv += optind;
146
147 if (argc == 0) {
148 fr_perror("radlock - Need file to operate on");
149 usage(64);
150 }
151
152 if (argc == 1) {
153 fr_perror("radlock - Need action, must be one of (lock|trylock|unlock|remove|info|perm)");
154 usage(64);
155 }
156
157 file = argv[0];
159 if (action == RADLOCK_INVALID) {
160 fr_perror("radlock - Action must be one of (lock|trylock|unlock|remove|info|perm), got %s", argv[1]);
161 usage(64);
162 }
163
164 if (action == RADLOCK_PERM) {
165 fr_perror("radlock - At least one of -u, -g, -m must be specified");
166 usage(64);
167 }
168
169 /*
170 * Mismatch between the binary and the libraries it depends on
171 */
173 fr_perror("radlock");
175 }
176
177 switch (action) {
178 case RADLOCK_LOCK:
179 case RADLOCK_TRYLOCK:
180 sem_id = fr_sem_get(file, 0, uid, gid, false, false);
181 if (sem_id < 0) {
182 fr_perror("radlock");
184 }
185 switch (fr_sem_wait(sem_id, file, false, action == RADLOCK_TRYLOCK)) {
186 case 1: /* Already locked */
187 {
188 pid_t pid;
189
190 fr_sem_pid(&pid, sem_id);
191 fr_perror("radlock - Can't lock \"%s\" already held by PID %u", file, pid);
193 }
194
195 case 0:
197
198 default:
199 break;
200 }
201 fr_perror("radlock");
203
204 case RADLOCK_UNLOCK:
205 sem_id = fr_sem_get(file, 0, uid, gid, false, true);
206 if (sem_id == -4) EXIT_WITH_SUCCESS;
207 if (sem_id < 0) {
208 fr_perror("radlock");
210 }
211
212 again:
213 switch (fr_sem_post(sem_id, file, false)) {
214 case 1: /* already unlocked */
216
217 case 0:
218 goto again;
219
220 default:
221 break;
222 }
223 fr_perror("radlock");
225
226 case RADLOCK_REMOVE:
227 sem_id = fr_sem_get(file, 0, uid, gid, false, true);
228 if (sem_id == -4) EXIT_WITH_SUCCESS;
229
230 if (fr_sem_close(sem_id, file) < 0) {
231 fr_perror("radlock");
233 }
235
236 case RADLOCK_INFO:
237 {
238 struct semid_ds info;
239 char buff[10];
240 unsigned int perm = S_IRUSR | S_IWUSR | S_IRGRP | S_IWGRP | S_IROTH | S_IWOTH;
241 bool dead = false;
242 pid_t pid;
243 int ret;
244 int value;
245 char const *uid_str, *gid_str, *cuid_str, *cgid_str;
246
247 sem_id = fr_sem_get(file, 0, uid, gid, false, true);
248 if (sem_id == -4) EXIT_WITH_FAILURE;
249 if (sem_id < 0) {
250 fr_perror("radlock");
252 }
253
254 if (semctl(sem_id, 0, IPC_STAT, &info) < 0) {
255 fr_perror("radlock - Failed getting lock info for \"%s\": %s", file, fr_syserror(errno));
257 }
258
259 if (fr_sem_pid(&pid, sem_id) < 0) {
260 fr_perror("radlock");
262 }
263
264 ret = kill(sem_id, 0);
265 if ((ret < 0) && (errno == ESRCH)) dead = true;
266
267 uid_str = fr_perm_uid_to_str(autofree, info.sem_perm.uid);
268 if (!uid_str) uid_str = "";
269
270 gid_str = fr_perm_gid_to_str(autofree, info.sem_perm.gid);
271 if (!gid_str) gid_str = "";
272
273 cuid_str = fr_perm_uid_to_str(autofree, info.sem_perm.cuid);
274 if (!cuid_str) cuid_str = "";
275
276 cgid_str = fr_perm_gid_to_str(autofree, info.sem_perm.cgid);
277 if (!cgid_str) cgid_str = "";
278
279 value = semctl(sem_id, 0, GETVAL);
280
281 INFO("Locking information for \"%s\"", file);
282 INFO("\tsemid : %u", sem_id);
283 INFO("\tPermissions : %s", fr_perm_mode_to_str(buff, info.sem_perm.mode & perm));
284 INFO("\tValue : %u (%s)", value, value > 0 ? "locked" : "unlocked");
285 INFO("Last Modified:");
286 INFO("\tPID : %u (%s)", pid, dead ? "dead" : "alive");
287 INFO("\tUser : %s (%u)", uid_str, info.sem_perm.uid);
288 INFO("\tGroup : %s (%u)", gid_str, info.sem_perm.gid);
289 INFO("\tTime : %s",
290 fr_asprintf(autofree, "%pV", fr_box_time(fr_time_from_sec(info.sem_otime))));
291 INFO("Created:");
292 INFO("\tUser : %s (%u)", cuid_str, info.sem_perm.cuid);
293 INFO("\tGroup : %s (%u)", cgid_str, info.sem_perm.cgid);
294 INFO("\tTime : %s",
295 fr_asprintf(autofree, "%pV", fr_box_time(fr_time_from_sec(info.sem_ctime))));
296 }
298
299 case RADLOCK_PERM:
300 {
301 struct semid_ds info;
302
303 sem_id = fr_sem_get(file, 0, uid, gid, false, false); /* Will create if does not already exist */
304 if (sem_id < 0) {
305 fr_perror("radlock");
307 }
308
309 if (semctl(sem_id, 0, IPC_STAT, &info) < 0) {
310 fr_perror("radlock - Failed getting lock info for \"%s\": %s",
311 fr_syserror(errno), file);
313 }
314
315 if (uid_set) info.sem_perm.uid = uid;
316 if (gid_set) info.sem_perm.gid = gid;
317 if (mode_set) info.sem_perm.mode = mode;
318
319 if (semctl(sem_id, 0, IPC_SET, &info) < 0) {
320 fr_perror("radlock - Failed setting lock permissions for \"%s\": %s",
321 fr_syserror(errno), file);
323 }
324 }
326
327 case RADLOCK_INVALID:
328 usage(64);
329 }
330
331 return 0;
332}
int const char * file
Definition acutest.h:702
#define RCSID(id)
Definition build.h:483
#define NEVER_RETURNS
Should be placed before the function return type.
Definition build.h:313
#define L(_str)
Helper for initialising arrays of string literals.
Definition build.h:209
#define DIAG_ON(_x)
Definition build.h:458
#define NUM_ELEMENTS(_t)
Definition build.h:337
#define DIAG_OFF(_x)
Definition build.h:457
int fr_fault_setup(TALLOC_CTX *ctx, char const *cmd, char const *program)
Registers signal handlers to execute panic_action on fatal signal.
Definition debug.c:1242
#define fr_exit(_x)
Exit, producing a log message in debug builds.
Definition debug.h:228
#define fr_exit_now(_x)
Exit without calling atexit() handlers, producing a log message in debug builds.
Definition debug.h:234
static NEVER_RETURNS void usage(void)
Definition dhcpclient.c:114
Test enumeration values.
Definition dict_test.h:92
char * fr_perm_uid_to_str(TALLOC_CTX *ctx, uid_t uid)
Print uid to a string.
Definition perm.c:494
char const * fr_perm_mode_to_str(char out[static 10], mode_t mode)
Convert mode_t into humanly readable permissions flags.
Definition perm.c:36
char * fr_perm_gid_to_str(TALLOC_CTX *ctx, uid_t gid)
Print gid to a string.
Definition perm.c:514
int fr_perm_uid_from_str(TALLOC_CTX *ctx, uid_t *out, char const *name)
Resolve a user name to a GID.
Definition perm.c:451
char * fr_asprintf(TALLOC_CTX *ctx, char const *fmt,...)
Special version of asprintf which implements custom format specifiers.
Definition print.c:874
static TALLOC_CTX * autofree
fr_radlock_action_t
Definition radlock.c:49
@ RADLOCK_TRYLOCK
Try and lock the semaphore and return if we can't.
Definition radlock.c:52
@ RADLOCK_UNLOCK
Unlock the semaphore.
Definition radlock.c:53
@ RADLOCK_PERM
Modify permissions for a given semaphore.
Definition radlock.c:56
@ RADLOCK_LOCK
Acquire the semaphore if it's at 0.
Definition radlock.c:51
@ RADLOCK_INVALID
Definition radlock.c:50
@ RADLOCK_REMOVE
Remove the semaphore.
Definition radlock.c:54
@ RADLOCK_INFO
Information about the semaphore.
Definition radlock.c:55
int main(int argc, char *argv[])
Definition radlock.c:88
static fr_table_num_sorted_t const radlock_action_table[]
Definition radlock.c:59
#define EXIT_WITH_SUCCESS
Definition radlock.c:82
#define EXIT_WITH_FAILURE
Definition radlock.c:81
#define INFO(fmt,...)
Definition radlock.c:46
static size_t radlock_action_table_len
Definition radlock.c:67
int fr_sem_post(int sem_id, char const *file, bool undo_on_exit)
Decrement the semaphore by 1.
Definition sem.c:182
int fr_sem_wait(int sem_id, char const *file, bool undo_on_exit, bool nonblock)
Wait for a semaphore to reach 0, then increment it by 1.
Definition sem.c:252
int fr_sem_close(int sem_id, char const *file)
Remove the semaphore, this helps with permissions issues.
Definition sem.c:329
int fr_sem_pid(pid_t *pid, int sem_id)
Return the PID of the process that last operated on the semaphore.
Definition sem.c:53
int fr_sem_get(char const *file, int proj_id, uid_t uid, gid_t gid, bool check_perm, bool must_exist)
Returns a semid for the semaphore associated with the file.
Definition sem.c:421
static char buff[sizeof("18446744073709551615")+3]
Definition size_tests.c:41
char const * fr_syserror(int num)
Guaranteed to be thread-safe version of strerror.
Definition syserror.c:243
#define fr_table_value_by_str(_table, _name, _def)
Convert a string to a value using a sorted or ordered table.
Definition table.h:653
An element in a lexicographically sorted array of name to num mappings.
Definition table.h:49
#define talloc_autofree_context
The original function is deprecated, so replace it with our version.
Definition talloc.h:51
static fr_time_t fr_time_from_sec(time_t when)
Convert a time_t (wallclock time) to a fr_time_t (internal time)
Definition time.h:858
void fr_perror(char const *fmt,...)
Print the current error to stderr with a prefix.
Definition strerror.c:733
int fr_check_lib_magic(uint64_t magic)
Check if the application linking to the library has the correct magic number.
Definition version.c:40
#define RADIUSD_MAGIC_NUMBER
Definition version.h:81
#define fr_box_time(_val)
Definition value.h:326