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: 60be089e8ec8d8d8c18a8d892ca75f6a05d867ee $
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: 60be089e8ec8d8d8c18a8d892ca75f6a05d867ee $")
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
33#include <sys/ipc.h>
34#include <sys/sem.h>
35
36#ifdef HAVE_GETOPT_H
37# include <getopt.h>
38#endif
39#include <assert.h>
40
41DIAG_OFF(unused-macros)
42#define INFO(fmt, ...) fprintf(stdout, fmt "\n", ## __VA_ARGS__)
43DIAG_ON(unused-macros)
44
45typedef enum {
47 RADLOCK_LOCK, //!< Acquire the semaphore if it's at 0.
48 RADLOCK_TRYLOCK, //!< Try and lock the semaphore and return if we can't.
49 RADLOCK_UNLOCK, //!< Unlock the semaphore.
50 RADLOCK_REMOVE, //!< Remove the semaphore.
51 RADLOCK_INFO, //!< Information about the semaphore.
52 RADLOCK_PERM //!< Modify permissions for a given semaphore.
54
56 { L("info"), RADLOCK_INFO },
57 { L("lock"), RADLOCK_LOCK },
58 { L("perm"), RADLOCK_PERM },
59 { L("remove"), RADLOCK_REMOVE },
60 { L("trylock"), RADLOCK_TRYLOCK },
61 { L("unlock"), RADLOCK_UNLOCK }
62};
64
65static NEVER_RETURNS void usage(int ret)
66{
67 fprintf(stderr, "usage: radlock <file> [lock|trylock|unlock|remove|info|perm]\n");
68 fprintf(stderr, " -u <uid> Desired user.\n");
69 fprintf(stderr, " -g <gid> Desired group.\n");
70 fprintf(stderr, " -m <perm> Octal permissions string.\n");
71 fprintf(stderr, " -h This help text.\n");
72 fprintf(stderr, "\n");
73 fprintf(stderr, "Simple utility to query the locking state of a config file\n");
74 fr_exit_now(ret);
75}
76
77#define EXIT_WITH_FAILURE exit(EXIT_FAILURE)
78#define EXIT_WITH_SUCCESS exit(EXIT_SUCCESS)
79
80/**
81 *
82 * @hidecallgraph
83 */
84int main(int argc, char *argv[])
85{
86 int c;
88 char const *file;
89 char *end;
90 uid_t uid = geteuid();
91 bool uid_set = false;
92 gid_t gid = getegid();
93 bool gid_set = false;
94 long mode = S_IRUSR | S_IWUSR | S_IRGRP | S_IWGRP | S_IROTH;
95 bool mode_set = false;
96 int sem_id;
97
98 TALLOC_CTX *autofree;
99
101
102#ifndef NDEBUG
103 if (fr_fault_setup(autofree, getenv("PANIC_ACTION"), argv[0]) < 0) {
104 fr_perror("radict");
105 fr_exit(EXIT_FAILURE);
106 }
107#endif
108
109 talloc_set_log_stderr();
110
111 while ((c = getopt(argc, argv, "u:g:m:h")) != -1) switch (c) {
112 case 'u':
113 if (fr_perm_uid_from_str(autofree, &uid, optarg) < 0) {
114 fr_perror("radlock");
116 }
117 uid_set = true;
118 break;
119
120 case 'g':
121 if (fr_perm_gid_from_str(autofree, &gid, optarg) < 0) {
122 fr_perror("radlock");
124 }
125 gid_set = true;
126 break;
127
128 case 'm':
129 mode = strtol(optarg, &end, 0); /* 0 base plus 0 prefix = octal */
130 if (*end || (end == optarg)) {
131 fr_perror("radlock - Bad mode value");
133 }
134 mode_set = true;
135 break;
136
137 case 'h':
138 default:
139 usage(EXIT_SUCCESS);
140 }
141 argc -= optind;
142 argv += optind;
143
144 if (argc == 0) {
145 fr_perror("radlock - Need file to operate on");
146 usage(64);
147 }
148
149 if (argc == 1) {
150 fr_perror("radlock - Need action, must be one of (lock|trylock|unlock|remove|info|perm)");
151 usage(64);
152 }
153
154 file = argv[0];
156 if (action == RADLOCK_INVALID) {
157 fr_perror("radlock - Action must be one of (lock|trylock|unlock|remove|info|perm), got %s", argv[1]);
158 usage(64);
159 }
160
161 if ((action == RADLOCK_PERM) && !uid_set && !gid_set && !mode_set) {
162 fr_perror("radlock - At least one of -u, -g, -m must be specified");
163 usage(64);
164 }
165
166 /*
167 * Mismatch between the binary and the libraries it depends on
168 */
170 fr_perror("radlock");
172 }
173
174 switch (action) {
175 case RADLOCK_LOCK:
176 case RADLOCK_TRYLOCK:
177 sem_id = fr_sem_get(file, 0, uid, gid, false, false);
178 if (sem_id < 0) {
179 fr_perror("radlock");
181 }
182 switch (fr_sem_wait(sem_id, file, false, action == RADLOCK_TRYLOCK)) {
183 case 1: /* Already locked */
184 {
185 pid_t pid;
186
187 fr_sem_pid(&pid, sem_id);
188 fr_perror("radlock - Can't lock \"%s\" already held by PID %u", file, pid);
190 }
191
192 case 0:
194
195 default:
196 break;
197 }
198 fr_perror("radlock");
200
201 case RADLOCK_UNLOCK:
202 sem_id = fr_sem_get(file, 0, uid, gid, false, true);
203 if (sem_id == -4) EXIT_WITH_SUCCESS;
204 if (sem_id < 0) {
205 fr_perror("radlock");
207 }
208
209 again:
210 switch (fr_sem_post(sem_id, file, false)) {
211 case 1: /* already unlocked */
213
214 case 0:
215 goto again;
216
217 default:
218 break;
219 }
220 fr_perror("radlock");
222
223 case RADLOCK_REMOVE:
224 sem_id = fr_sem_get(file, 0, uid, gid, false, true);
225 if (sem_id == -4) EXIT_WITH_SUCCESS;
226
227 if (fr_sem_close(sem_id, file) < 0) {
228 fr_perror("radlock");
230 }
232
233 case RADLOCK_INFO:
234 {
235 struct semid_ds info;
236 char buff[10];
237 unsigned int perm = S_IRUSR | S_IWUSR | S_IRGRP | S_IWGRP | S_IROTH | S_IWOTH;
238 bool dead = false;
239 pid_t pid;
240 int ret;
241 int value;
242 char const *uid_str, *gid_str, *cuid_str, *cgid_str;
243
244 sem_id = fr_sem_get(file, 0, uid, gid, false, true);
245 if (sem_id == -4) EXIT_WITH_FAILURE;
246 if (sem_id < 0) {
247 fr_perror("radlock");
249 }
250
251 if (semctl(sem_id, 0, IPC_STAT, &info) < 0) {
252 fr_perror("radlock - Failed getting lock info for \"%s\": %s", file, fr_syserror(errno));
254 }
255
256 if (fr_sem_pid(&pid, sem_id) < 0) {
257 fr_perror("radlock");
259 }
260
261 ret = kill(pid, 0);
262 if ((ret < 0) && (errno == ESRCH)) dead = true;
263
264 uid_str = fr_perm_uid_to_str(autofree, info.sem_perm.uid);
265 if (!uid_str) uid_str = "";
266
267 gid_str = fr_perm_gid_to_str(autofree, info.sem_perm.gid);
268 if (!gid_str) gid_str = "";
269
270 cuid_str = fr_perm_uid_to_str(autofree, info.sem_perm.cuid);
271 if (!cuid_str) cuid_str = "";
272
273 cgid_str = fr_perm_gid_to_str(autofree, info.sem_perm.cgid);
274 if (!cgid_str) cgid_str = "";
275
276 value = semctl(sem_id, 0, GETVAL);
277
278 INFO("Locking information for \"%s\"", file);
279 INFO("\tsemid : %u", sem_id);
280 INFO("\tPermissions : %s", fr_perm_mode_to_str(buff, info.sem_perm.mode & perm));
281 INFO("\tValue : %u (%s)", value, value > 0 ? "locked" : "unlocked");
282 INFO("Last Modified:");
283 INFO("\tPID : %u (%s)", pid, dead ? "dead" : "alive");
284 INFO("\tUser : %s (%u)", uid_str, info.sem_perm.uid);
285 INFO("\tGroup : %s (%u)", gid_str, info.sem_perm.gid);
286 INFO("\tTime : %s",
287 fr_asprintf(autofree, "%pV", fr_box_time(fr_time_from_sec(info.sem_otime))));
288 INFO("Created:");
289 INFO("\tUser : %s (%u)", cuid_str, info.sem_perm.cuid);
290 INFO("\tGroup : %s (%u)", cgid_str, info.sem_perm.cgid);
291 INFO("\tTime : %s",
292 fr_asprintf(autofree, "%pV", fr_box_time(fr_time_from_sec(info.sem_ctime))));
293 }
295
296 case RADLOCK_PERM:
297 {
298 struct semid_ds info;
299
300 sem_id = fr_sem_get(file, 0, uid, gid, false, false); /* Will create if does not already exist */
301 if (sem_id < 0) {
302 fr_perror("radlock");
304 }
305
306 if (semctl(sem_id, 0, IPC_STAT, &info) < 0) {
307 fr_perror("radlock - Failed getting lock info for \"%s\": %s",
308 file, fr_syserror(errno));
310 }
311
312 if (uid_set) info.sem_perm.uid = uid;
313 if (gid_set) info.sem_perm.gid = gid;
314 if (mode_set) info.sem_perm.mode = mode;
315
316 if (semctl(sem_id, 0, IPC_SET, &info) < 0) {
317 fr_perror("radlock - Failed setting lock permissions for \"%s\": %s",
318 file, fr_syserror(errno));
320 }
321 }
323
324 case RADLOCK_INVALID:
325 usage(64);
326 }
327
328 return 0;
329}
int const char * file
Definition acutest.h:702
static TALLOC_CTX * autofree
Definition fuzzer.c:44
#define RCSID(id)
Definition build.h:506
#define NEVER_RETURNS
Should be placed before the function return type.
Definition build.h:334
#define L(_str)
Helper for initialising arrays of string literals.
Definition build.h:228
#define DIAG_ON(_x)
Definition build.h:481
#define NUM_ELEMENTS(_t)
Definition build.h:358
#define DIAG_OFF(_x)
Definition build.h:480
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:230
#define fr_exit_now(_x)
Exit without calling atexit() handlers, producing a log message in debug builds.
Definition debug.h:236
static NEVER_RETURNS void usage(void)
Definition dhcpclient.c:113
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:496
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:516
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:453
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:475
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:45
@ RADLOCK_TRYLOCK
Try and lock the semaphore and return if we can't.
Definition radlock.c:48
@ RADLOCK_UNLOCK
Unlock the semaphore.
Definition radlock.c:49
@ RADLOCK_PERM
Modify permissions for a given semaphore.
Definition radlock.c:52
@ RADLOCK_LOCK
Acquire the semaphore if it's at 0.
Definition radlock.c:47
@ RADLOCK_INVALID
Definition radlock.c:46
@ RADLOCK_REMOVE
Remove the semaphore.
Definition radlock.c:50
@ RADLOCK_INFO
Information about the semaphore.
Definition radlock.c:51
int main(int argc, char *argv[])
Definition radlock.c:84
static fr_table_num_sorted_t const radlock_action_table[]
Definition radlock.c:55
#define EXIT_WITH_SUCCESS
Definition radlock.c:78
#define EXIT_WITH_FAILURE
Definition radlock.c:77
#define INFO(fmt,...)
Definition radlock.c:42
static size_t radlock_action_table_len
Definition radlock.c:63
int fr_sem_post(int sem_id, char const *file, bool undo_on_exit)
Decrement the semaphore by 1.
Definition sem.c:181
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:251
int fr_sem_close(int sem_id, char const *file)
Remove the semaphore, this helps with permissions issues.
Definition sem.c:328
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:52
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:420
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:48
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