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: 93dc05260b7ce9dec2d77a1ec2958d61679ba535 $
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: 93dc05260b7ce9dec2d77a1ec2958d61679ba535 $")
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 int c;
92 char const *file;
93 char *end;
94 uid_t uid = geteuid();
95 bool uid_set = false;
96 gid_t gid = getegid();
97 bool gid_set = false;
98 long mode = S_IRUSR | S_IWUSR | S_IRGRP | S_IWGRP | S_IROTH;
99 bool mode_set = false;
100 int sem_id;
101
102 TALLOC_CTX *autofree;
103
105
106#ifndef NDEBUG
107 if (fr_fault_setup(autofree, getenv("PANIC_ACTION"), argv[0]) < 0) {
108 fr_perror("radict");
109 fr_exit(EXIT_FAILURE);
110 }
111#endif
112
113 talloc_set_log_stderr();
114
115 while ((c = getopt(argc, argv, "u:g:m:h")) != -1) switch (c) {
116 case 'u':
117 if (fr_perm_uid_from_str(autofree, &uid, optarg) < 0) {
118 fr_perror("radlock");
120 }
121 uid_set = true;
122 break;
123
124 case 'g':
125 if (fr_perm_gid_from_str(autofree, &gid, optarg) < 0) {
126 fr_perror("radlock");
128 }
129 gid_set = true;
130 break;
131
132 case 'm':
133 mode = strtol(optarg, &end, 0); /* 0 base plus 0 prefix = octal */
134 if (*end || (end == optarg)) {
135 fr_perror("radlock - Bad mode value");
137 }
138 mode_set = true;
139 break;
140
141 case 'h':
142 default:
143 usage(EXIT_SUCCESS);
144 }
145 argc -= optind;
146 argv += optind;
147
148 if (argc == 0) {
149 fr_perror("radlock - Need file to operate on");
150 usage(64);
151 }
152
153 if (argc == 1) {
154 fr_perror("radlock - Need action, must be one of (lock|trylock|unlock|remove|info|perm)");
155 usage(64);
156 }
157
158 file = argv[0];
160 if (action == RADLOCK_INVALID) {
161 fr_perror("radlock - Action must be one of (lock|trylock|unlock|remove|info|perm), got %s", argv[1]);
162 usage(64);
163 }
164
165 if ((action == RADLOCK_PERM) && !uid_set && !gid_set && !mode_set) {
166 fr_perror("radlock - At least one of -u, -g, -m must be specified");
167 usage(64);
168 }
169
170 /*
171 * Mismatch between the binary and the libraries it depends on
172 */
174 fr_perror("radlock");
176 }
177
178 switch (action) {
179 case RADLOCK_LOCK:
180 case RADLOCK_TRYLOCK:
181 sem_id = fr_sem_get(file, 0, uid, gid, false, false);
182 if (sem_id < 0) {
183 fr_perror("radlock");
185 }
186 switch (fr_sem_wait(sem_id, file, false, action == RADLOCK_TRYLOCK)) {
187 case 1: /* Already locked */
188 {
189 pid_t pid;
190
191 fr_sem_pid(&pid, sem_id);
192 fr_perror("radlock - Can't lock \"%s\" already held by PID %u", file, pid);
194 }
195
196 case 0:
198
199 default:
200 break;
201 }
202 fr_perror("radlock");
204
205 case RADLOCK_UNLOCK:
206 sem_id = fr_sem_get(file, 0, uid, gid, false, true);
207 if (sem_id == -4) EXIT_WITH_SUCCESS;
208 if (sem_id < 0) {
209 fr_perror("radlock");
211 }
212
213 again:
214 switch (fr_sem_post(sem_id, file, false)) {
215 case 1: /* already unlocked */
217
218 case 0:
219 goto again;
220
221 default:
222 break;
223 }
224 fr_perror("radlock");
226
227 case RADLOCK_REMOVE:
228 sem_id = fr_sem_get(file, 0, uid, gid, false, true);
229 if (sem_id == -4) EXIT_WITH_SUCCESS;
230
231 if (fr_sem_close(sem_id, file) < 0) {
232 fr_perror("radlock");
234 }
236
237 case RADLOCK_INFO:
238 {
239 struct semid_ds info;
240 char buff[10];
241 unsigned int perm = S_IRUSR | S_IWUSR | S_IRGRP | S_IWGRP | S_IROTH | S_IWOTH;
242 bool dead = false;
243 pid_t pid;
244 int ret;
245 int value;
246 char const *uid_str, *gid_str, *cuid_str, *cgid_str;
247
248 sem_id = fr_sem_get(file, 0, uid, gid, false, true);
249 if (sem_id == -4) EXIT_WITH_FAILURE;
250 if (sem_id < 0) {
251 fr_perror("radlock");
253 }
254
255 if (semctl(sem_id, 0, IPC_STAT, &info) < 0) {
256 fr_perror("radlock - Failed getting lock info for \"%s\": %s", file, fr_syserror(errno));
258 }
259
260 if (fr_sem_pid(&pid, sem_id) < 0) {
261 fr_perror("radlock");
263 }
264
265 ret = kill(pid, 0);
266 if ((ret < 0) && (errno == ESRCH)) dead = true;
267
268 uid_str = fr_perm_uid_to_str(autofree, info.sem_perm.uid);
269 if (!uid_str) uid_str = "";
270
271 gid_str = fr_perm_gid_to_str(autofree, info.sem_perm.gid);
272 if (!gid_str) gid_str = "";
273
274 cuid_str = fr_perm_uid_to_str(autofree, info.sem_perm.cuid);
275 if (!cuid_str) cuid_str = "";
276
277 cgid_str = fr_perm_gid_to_str(autofree, info.sem_perm.cgid);
278 if (!cgid_str) cgid_str = "";
279
280 value = semctl(sem_id, 0, GETVAL);
281
282 INFO("Locking information for \"%s\"", file);
283 INFO("\tsemid : %u", sem_id);
284 INFO("\tPermissions : %s", fr_perm_mode_to_str(buff, info.sem_perm.mode & perm));
285 INFO("\tValue : %u (%s)", value, value > 0 ? "locked" : "unlocked");
286 INFO("Last Modified:");
287 INFO("\tPID : %u (%s)", pid, dead ? "dead" : "alive");
288 INFO("\tUser : %s (%u)", uid_str, info.sem_perm.uid);
289 INFO("\tGroup : %s (%u)", gid_str, info.sem_perm.gid);
290 INFO("\tTime : %s",
291 fr_asprintf(autofree, "%pV", fr_box_time(fr_time_from_sec(info.sem_otime))));
292 INFO("Created:");
293 INFO("\tUser : %s (%u)", cuid_str, info.sem_perm.cuid);
294 INFO("\tGroup : %s (%u)", cgid_str, info.sem_perm.cgid);
295 INFO("\tTime : %s",
296 fr_asprintf(autofree, "%pV", fr_box_time(fr_time_from_sec(info.sem_ctime))));
297 }
299
300 case RADLOCK_PERM:
301 {
302 struct semid_ds info;
303
304 sem_id = fr_sem_get(file, 0, uid, gid, false, false); /* Will create if does not already exist */
305 if (sem_id < 0) {
306 fr_perror("radlock");
308 }
309
310 if (semctl(sem_id, 0, IPC_STAT, &info) < 0) {
311 fr_perror("radlock - Failed getting lock info for \"%s\": %s",
312 file, fr_syserror(errno));
314 }
315
316 if (uid_set) info.sem_perm.uid = uid;
317 if (gid_set) info.sem_perm.gid = gid;
318 if (mode_set) info.sem_perm.mode = mode;
319
320 if (semctl(sem_id, 0, IPC_SET, &info) < 0) {
321 fr_perror("radlock - Failed setting lock permissions for \"%s\": %s",
322 file, fr_syserror(errno));
324 }
325 }
327
328 case RADLOCK_INVALID:
329 usage(64);
330 }
331
332 return 0;
333}
int const char * file
Definition acutest.h:704
static TALLOC_CTX * autofree
Definition fuzzer.c:46
#define RCSID(id)
Definition build.h:487
#define NEVER_RETURNS
Should be placed before the function return type.
Definition build.h:315
#define L(_str)
Helper for initialising arrays of string literals.
Definition build.h:209
#define DIAG_ON(_x)
Definition build.h:462
#define NUM_ELEMENTS(_t)
Definition build.h:339
#define DIAG_OFF(_x)
Definition build.h:461
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:1056
#define fr_exit(_x)
Exit, producing a log message in debug builds.
Definition debug.h:220
#define fr_exit_now(_x)
Exit without calling atexit() handlers, producing a log message in debug builds.
Definition debug.h:226
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
int fr_perm_gid_from_str(TALLOC_CTX *ctx, gid_t *out, char const *name)
Resolve a group name to a GID.
Definition perm.c:473
char * fr_asprintf(TALLOC_CTX *ctx, char const *fmt,...)
Special version of asprintf which implements custom format specifiers.
Definition print.c:883
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:732
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:349