]> git.ipfire.org Git - thirdparty/systemd.git/blob - src/shared/user-record-show.c
de04859a2f10db9829236e9e5f12660603ecf2fb
[thirdparty/systemd.git] / src / shared / user-record-show.c
1 /* SPDX-License-Identifier: LGPL-2.1+ */
2
3 #include "format-util.h"
4 #include "fs-util.h"
5 #include "group-record.h"
6 #include "process-util.h"
7 #include "rlimit-util.h"
8 #include "strv.h"
9 #include "terminal-util.h"
10 #include "user-record-show.h"
11 #include "user-util.h"
12 #include "userdb.h"
13
14 const char *user_record_state_color(const char *state) {
15 if (STR_IN_SET(state, "unfixated", "absent"))
16 return ansi_grey();
17 else if (streq(state, "active"))
18 return ansi_highlight_green();
19 else if (streq(state, "locked"))
20 return ansi_highlight_yellow();
21
22 return NULL;
23 }
24
25 void user_record_show(UserRecord *hr, bool show_full_group_info) {
26 const char *hd, *ip, *shell;
27 UserStorage storage;
28 usec_t t;
29 size_t k;
30 int r, b;
31
32 printf(" User name: %s\n",
33 user_record_user_name_and_realm(hr));
34
35 if (hr->state) {
36 const char *color;
37
38 color = user_record_state_color(hr->state);
39
40 printf(" State: %s%s%s\n",
41 strempty(color), hr->state, color ? ansi_normal() : "");
42 }
43
44 printf(" Disposition: %s\n", user_disposition_to_string(user_record_disposition(hr)));
45
46 if (hr->last_change_usec != USEC_INFINITY) {
47 char buf[FORMAT_TIMESTAMP_MAX];
48 printf(" Last Change: %s\n", format_timestamp(buf, sizeof(buf), hr->last_change_usec));
49 }
50
51 if (hr->last_password_change_usec != USEC_INFINITY &&
52 hr->last_password_change_usec != hr->last_change_usec) {
53 char buf[FORMAT_TIMESTAMP_MAX];
54 printf(" Last Passw.: %s\n", format_timestamp(buf, sizeof(buf), hr->last_password_change_usec));
55 }
56
57 r = user_record_test_blocked(hr);
58 switch (r) {
59
60 case -ESTALE:
61 printf(" Login OK: %sno%s (last change time is in the future)\n", ansi_highlight_red(), ansi_normal());
62 break;
63
64 case -ENOLCK:
65 printf(" Login OK: %sno%s (record is locked)\n", ansi_highlight_red(), ansi_normal());
66 break;
67
68 case -EL2HLT:
69 printf(" Login OK: %sno%s (record not valid yet))\n", ansi_highlight_red(), ansi_normal());
70 break;
71
72 case -EL3HLT:
73 printf(" Login OK: %sno%s (record not valid anymore))\n", ansi_highlight_red(), ansi_normal());
74 break;
75
76 default: {
77 usec_t y;
78
79 if (r < 0) {
80 errno = -r;
81 printf(" Login OK: %sno%s (%m)\n", ansi_highlight_red(), ansi_normal());
82 break;
83 }
84
85 if (is_nologin_shell(user_record_shell(hr))) {
86 printf(" Login OK: %sno%s (nologin shell)\n", ansi_highlight_red(), ansi_normal());
87 break;
88 }
89
90 y = user_record_ratelimit_next_try(hr);
91 if (y != USEC_INFINITY && y > now(CLOCK_REALTIME)) {
92 printf(" Login OK: %sno%s (ratelimit)\n", ansi_highlight_red(), ansi_normal());
93 break;
94 }
95
96 printf(" Login OK: %syes%s\n", ansi_highlight_green(), ansi_normal());
97 break;
98 }}
99
100 r = user_record_test_password_change_required(hr);
101 switch (r) {
102
103 case -EKEYREVOKED:
104 printf(" Password OK: %schange now%s\n", ansi_highlight_yellow(), ansi_normal());
105 break;
106
107 case -EOWNERDEAD:
108 printf(" Password OK: %sexpired%s (change now!)\n", ansi_highlight_yellow(), ansi_normal());
109 break;
110
111 case -EKEYREJECTED:
112 printf(" Password OK: %sexpired%s (for good)\n", ansi_highlight_red(), ansi_normal());
113 break;
114
115 case -EKEYEXPIRED:
116 printf(" Password OK: %sexpires soon%s\n", ansi_highlight_yellow(), ansi_normal());
117 break;
118
119 case -ENETDOWN:
120 printf(" Password OK: %sno timestamp%s\n", ansi_highlight_red(), ansi_normal());
121 break;
122
123 case -EROFS:
124 printf(" Password OK: %schange not permitted%s\n", ansi_highlight_yellow(), ansi_normal());
125 break;
126
127 default:
128 if (r < 0) {
129 errno = -r;
130 printf(" Password OK: %sno%s (%m)\n", ansi_highlight_yellow(), ansi_normal());
131 break;
132 }
133
134 printf(" Password OK: %syes%s\n", ansi_highlight_green(), ansi_normal());
135 break;
136 }
137
138 if (uid_is_valid(hr->uid))
139 printf(" UID: " UID_FMT "\n", hr->uid);
140 if (gid_is_valid(hr->gid)) {
141 if (show_full_group_info) {
142 _cleanup_(group_record_unrefp) GroupRecord *gr = NULL;
143
144 r = groupdb_by_gid(hr->gid, 0, &gr);
145 if (r < 0) {
146 errno = -r;
147 printf(" GID: " GID_FMT " (unresolvable: %m)\n", hr->gid);
148 } else
149 printf(" GID: " GID_FMT " (%s)\n", hr->gid, gr->group_name);
150 } else
151 printf(" GID: " GID_FMT "\n", hr->gid);
152 } else if (uid_is_valid(hr->uid)) /* Show UID as GID if not separately configured */
153 printf(" GID: " GID_FMT "\n", (gid_t) hr->uid);
154
155 if (show_full_group_info) {
156 _cleanup_(userdb_iterator_freep) UserDBIterator *iterator = NULL;
157
158 r = membershipdb_by_user(hr->user_name, 0, &iterator);
159 if (r < 0) {
160 errno = -r;
161 printf(" Aux. Groups: (can't acquire: %m)\n");
162 } else {
163 const char *prefix = " Aux. Groups:";
164
165 for (;;) {
166 _cleanup_free_ char *group = NULL;
167
168 r = membershipdb_iterator_get(iterator, NULL, &group);
169 if (r == -ESRCH)
170 break;
171 if (r < 0) {
172 errno = -r;
173 printf("%s (can't iterate: %m)\n", prefix);
174 break;
175 }
176
177 printf("%s %s\n", prefix, group);
178 prefix = " ";
179 }
180 }
181 }
182
183 if (hr->real_name && !streq(hr->real_name, hr->user_name))
184 printf(" Real Name: %s\n", hr->real_name);
185
186 hd = user_record_home_directory(hr);
187 if (hd)
188 printf(" Directory: %s\n", hd);
189
190 storage = user_record_storage(hr);
191 if (storage >= 0) /* Let's be political, and clarify which storage we like, and which we don't. About CIFS we don't complain. */
192 printf(" Storage: %s%s\n", user_storage_to_string(storage),
193 storage == USER_LUKS ? " (strong encryption)" :
194 storage == USER_FSCRYPT ? " (weak encryption)" :
195 IN_SET(storage, USER_DIRECTORY, USER_SUBVOLUME) ? " (no encryption)" : "");
196
197 ip = user_record_image_path(hr);
198 if (ip && !streq_ptr(ip, hd))
199 printf(" Image Path: %s\n", ip);
200
201 b = user_record_removable(hr);
202 if (b >= 0)
203 printf(" Removable: %s\n", yes_no(b));
204
205 shell = user_record_shell(hr);
206 if (shell)
207 printf(" Shell: %s\n", shell);
208
209 if (hr->email_address)
210 printf(" Email: %s\n", hr->email_address);
211 if (hr->location)
212 printf(" Location: %s\n", hr->location);
213 if (hr->password_hint)
214 printf(" Passw. Hint: %s\n", hr->password_hint);
215 if (hr->icon_name)
216 printf(" Icon Name: %s\n", hr->icon_name);
217
218 if (hr->time_zone)
219 printf(" Time Zone: %s\n", hr->time_zone);
220
221 if (hr->preferred_language)
222 printf(" Language: %s\n", hr->preferred_language);
223
224 if (!strv_isempty(hr->environment)) {
225 char **i;
226
227 STRV_FOREACH(i, hr->environment) {
228 printf(i == hr->environment ?
229 " Environment: %s\n" :
230 " %s\n", *i);
231 }
232 }
233
234 if (hr->locked >= 0)
235 printf(" Locked: %s\n", yes_no(hr->locked));
236
237 if (hr->not_before_usec != UINT64_MAX) {
238 char buf[FORMAT_TIMESTAMP_MAX];
239 printf(" Not Before: %s\n", format_timestamp(buf, sizeof(buf), hr->not_before_usec));
240 }
241
242 if (hr->not_after_usec != UINT64_MAX) {
243 char buf[FORMAT_TIMESTAMP_MAX];
244 printf(" Not After: %s\n", format_timestamp(buf, sizeof(buf), hr->not_after_usec));
245 }
246
247 if (hr->umask != MODE_INVALID)
248 printf(" UMask: 0%03o\n", hr->umask);
249
250 if (nice_is_valid(hr->nice_level))
251 printf(" Nice: %i\n", hr->nice_level);
252
253 for (int j = 0; j < _RLIMIT_MAX; j++) {
254 if (hr->rlimits[j])
255 printf(" Limit: RLIMIT_%s=%" PRIu64 ":%" PRIu64 "\n",
256 rlimit_to_string(j), (uint64_t) hr->rlimits[j]->rlim_cur, (uint64_t) hr->rlimits[j]->rlim_max);
257 }
258
259 if (hr->tasks_max != UINT64_MAX)
260 printf(" Tasks Max: %" PRIu64 "\n", hr->tasks_max);
261
262 if (hr->memory_high != UINT64_MAX) {
263 char buf[FORMAT_BYTES_MAX];
264 printf(" Memory High: %s\n", format_bytes(buf, sizeof(buf), hr->memory_high));
265 }
266
267 if (hr->memory_max != UINT64_MAX) {
268 char buf[FORMAT_BYTES_MAX];
269 printf(" Memory Max: %s\n", format_bytes(buf, sizeof(buf), hr->memory_max));
270 }
271
272 if (hr->cpu_weight != UINT64_MAX)
273 printf(" CPU Weight: %" PRIu64 "\n", hr->cpu_weight);
274
275 if (hr->io_weight != UINT64_MAX)
276 printf(" IO Weight: %" PRIu64 "\n", hr->io_weight);
277
278 if (hr->access_mode != MODE_INVALID)
279 printf(" Access Mode: 0%03oo\n", user_record_access_mode(hr));
280
281 if (storage == USER_LUKS) {
282 printf("LUKS Discard: online=%s offline=%s\n", yes_no(user_record_luks_discard(hr)), yes_no(user_record_luks_offline_discard(hr)));
283
284 if (!sd_id128_is_null(hr->luks_uuid))
285 printf(" LUKS UUID: " SD_ID128_FORMAT_STR "\n", SD_ID128_FORMAT_VAL(hr->luks_uuid));
286 if (!sd_id128_is_null(hr->partition_uuid))
287 printf(" Part UUID: " SD_ID128_FORMAT_STR "\n", SD_ID128_FORMAT_VAL(hr->partition_uuid));
288 if (!sd_id128_is_null(hr->file_system_uuid))
289 printf(" FS UUID: " SD_ID128_FORMAT_STR "\n", SD_ID128_FORMAT_VAL(hr->file_system_uuid));
290
291 if (hr->file_system_type)
292 printf(" File System: %s\n", user_record_file_system_type(hr));
293
294 if (hr->luks_cipher)
295 printf(" LUKS Cipher: %s\n", hr->luks_cipher);
296 if (hr->luks_cipher_mode)
297 printf(" Cipher Mode: %s\n", hr->luks_cipher_mode);
298 if (hr->luks_volume_key_size != UINT64_MAX)
299 printf(" Volume Key: %" PRIu64 "bit\n", hr->luks_volume_key_size * 8);
300
301 if (hr->luks_pbkdf_type)
302 printf(" PBKDF Type: %s\n", hr->luks_pbkdf_type);
303 if (hr->luks_pbkdf_hash_algorithm)
304 printf(" PBKDF Hash: %s\n", hr->luks_pbkdf_hash_algorithm);
305 if (hr->luks_pbkdf_time_cost_usec != UINT64_MAX) {
306 char buf[FORMAT_TIMESPAN_MAX];
307 printf(" PBKDF Time: %s\n", format_timespan(buf, sizeof(buf), hr->luks_pbkdf_time_cost_usec, 0));
308 }
309 if (hr->luks_pbkdf_memory_cost != UINT64_MAX) {
310 char buf[FORMAT_BYTES_MAX];
311 printf(" PBKDF Bytes: %s\n", format_bytes(buf, sizeof(buf), hr->luks_pbkdf_memory_cost));
312 }
313 if (hr->luks_pbkdf_parallel_threads != UINT64_MAX)
314 printf("PBKDF Thread: %" PRIu64 "\n", hr->luks_pbkdf_parallel_threads);
315
316 } else if (storage == USER_CIFS) {
317
318 if (hr->cifs_service)
319 printf("CIFS Service: %s\n", hr->cifs_service);
320 }
321
322 if (hr->cifs_user_name)
323 printf(" CIFS User: %s\n", user_record_cifs_user_name(hr));
324 if (hr->cifs_domain)
325 printf(" CIFS Domain: %s\n", hr->cifs_domain);
326
327 if (storage != USER_CLASSIC)
328 printf(" Mount Flags: %s %s %s\n",
329 hr->nosuid ? "nosuid" : "suid",
330 hr->nodev ? "nodev" : "dev",
331 hr->noexec ? "noexec" : "exec");
332
333 if (hr->skeleton_directory)
334 printf(" Skel. Dir.: %s\n", user_record_skeleton_directory(hr));
335
336 if (hr->disk_size != UINT64_MAX) {
337 char buf[FORMAT_BYTES_MAX];
338 printf(" Disk Size: %s\n", format_bytes(buf, sizeof(buf), hr->disk_size));
339 }
340
341 if (hr->disk_usage != UINT64_MAX) {
342 char buf[FORMAT_BYTES_MAX];
343
344 if (hr->disk_size != UINT64_MAX) {
345 unsigned permille;
346
347 permille = (unsigned) DIV_ROUND_UP(hr->disk_usage * 1000U, hr->disk_size); /* Round up! */
348 printf(" Disk Usage: %s (= %u.%01u%%)\n",
349 format_bytes(buf, sizeof(buf), hr->disk_usage),
350 permille / 10, permille % 10);
351 } else
352 printf(" Disk Usage: %s\n", format_bytes(buf, sizeof(buf), hr->disk_usage));
353 }
354
355 if (hr->disk_free != UINT64_MAX) {
356 char buf[FORMAT_BYTES_MAX];
357
358 if (hr->disk_size != UINT64_MAX) {
359 const char *color_on, *color_off;
360 unsigned permille;
361
362 permille = (unsigned) ((hr->disk_free * 1000U) / hr->disk_size); /* Round down! */
363
364 /* Color the output red or yellow if we are below 10% resp. 25% free. Because 10% and
365 * 25% can be a lot of space still, let's additionally make some absolute
366 * restrictions: 1G and 2G */
367 if (permille <= 100U &&
368 hr->disk_free < 1024U*1024U*1024U /* 1G */) {
369 color_on = ansi_highlight_red();
370 color_off = ansi_normal();
371 } else if (permille <= 250U &&
372 hr->disk_free < 2U*1024U*1024U*1024U /* 2G */) {
373 color_on = ansi_highlight_yellow();
374 color_off = ansi_normal();
375 } else
376 color_on = color_off = "";
377
378 printf(" Disk Free: %s%s (= %u.%01u%%)%s\n",
379 color_on,
380 format_bytes(buf, sizeof(buf), hr->disk_free),
381 permille / 10, permille % 10,
382 color_off);
383 } else
384 printf(" Disk Free: %s\n", format_bytes(buf, sizeof(buf), hr->disk_free));
385 }
386
387 if (hr->disk_floor != UINT64_MAX) {
388 char buf[FORMAT_BYTES_MAX];
389 printf(" Disk Floor: %s\n", format_bytes(buf, sizeof(buf), hr->disk_floor));
390 }
391
392 if (hr->disk_ceiling != UINT64_MAX) {
393 char buf[FORMAT_BYTES_MAX];
394 printf("Disk Ceiling: %s\n", format_bytes(buf, sizeof(buf), hr->disk_ceiling));
395 }
396
397 if (hr->good_authentication_counter != UINT64_MAX)
398 printf(" Good Auth.: %" PRIu64 "\n", hr->good_authentication_counter);
399
400 if (hr->last_good_authentication_usec != UINT64_MAX) {
401 char buf[FORMAT_TIMESTAMP_MAX];
402 printf(" Last Good: %s\n", format_timestamp(buf, sizeof(buf), hr->last_good_authentication_usec));
403 }
404
405 if (hr->bad_authentication_counter != UINT64_MAX)
406 printf(" Bad Auth.: %" PRIu64 "\n", hr->bad_authentication_counter);
407
408 if (hr->last_bad_authentication_usec != UINT64_MAX) {
409 char buf[FORMAT_TIMESTAMP_MAX];
410 printf(" Last Bad: %s\n", format_timestamp(buf, sizeof(buf), hr->last_bad_authentication_usec));
411 }
412
413 t = user_record_ratelimit_next_try(hr);
414 if (t != USEC_INFINITY) {
415 usec_t n = now(CLOCK_REALTIME);
416
417 if (t <= n)
418 printf(" Next Try: anytime\n");
419 else {
420 char buf[FORMAT_TIMESPAN_MAX];
421 printf(" Next Try: %sin %s%s\n",
422 ansi_highlight_red(),
423 format_timespan(buf, sizeof(buf), t - n, USEC_PER_SEC),
424 ansi_normal());
425 }
426 }
427
428 if (storage != USER_CLASSIC) {
429 char buf[FORMAT_TIMESPAN_MAX];
430 printf(" Auth. Limit: %" PRIu64 " attempts per %s\n", user_record_ratelimit_burst(hr),
431 format_timespan(buf, sizeof(buf), user_record_ratelimit_interval_usec(hr), 0));
432 }
433
434 if (hr->enforce_password_policy >= 0)
435 printf(" Passwd Pol.: %s\n", yes_no(hr->enforce_password_policy));
436
437 if (hr->password_change_min_usec != UINT64_MAX ||
438 hr->password_change_max_usec != UINT64_MAX ||
439 hr->password_change_warn_usec != UINT64_MAX ||
440 hr->password_change_inactive_usec != UINT64_MAX) {
441
442 char buf[FORMAT_TIMESPAN_MAX];
443 printf(" Passwd Chg.:");
444
445 if (hr->password_change_min_usec != UINT64_MAX) {
446 printf(" min %s", format_timespan(buf, sizeof(buf), hr->password_change_min_usec, 0));
447
448 if (hr->password_change_max_usec != UINT64_MAX)
449 printf(" …");
450 }
451
452 if (hr->password_change_max_usec != UINT64_MAX)
453 printf(" max %s", format_timespan(buf, sizeof(buf), hr->password_change_max_usec, 0));
454
455 if (hr->password_change_warn_usec != UINT64_MAX)
456 printf("/warn %s", format_timespan(buf, sizeof(buf), hr->password_change_warn_usec, 0));
457
458 if (hr->password_change_inactive_usec != UINT64_MAX)
459 printf("/inactive %s", format_timespan(buf, sizeof(buf), hr->password_change_inactive_usec, 0));
460
461 printf("\n");
462 }
463
464 if (hr->password_change_now >= 0)
465 printf("Pas. Ch. Now: %s\n", yes_no(hr->password_change_now));
466
467 if (!strv_isempty(hr->ssh_authorized_keys))
468 printf("SSH Pub. Key: %zu\n", strv_length(hr->ssh_authorized_keys));
469
470 if (!strv_isempty(hr->pkcs11_token_uri)) {
471 char **i;
472
473 STRV_FOREACH(i, hr->pkcs11_token_uri)
474 printf(i == hr->pkcs11_token_uri ?
475 " Sec. Token: %s\n" :
476 " %s\n", *i);
477 }
478
479 k = strv_length(hr->hashed_password);
480 if (k == 0)
481 printf(" Passwords: %snone%s\n",
482 user_record_disposition(hr) == USER_REGULAR ? ansi_highlight_yellow() : ansi_normal(), ansi_normal());
483 else
484 printf(" Passwords: %zu\n", k);
485
486 if (hr->signed_locally >= 0)
487 printf(" Local Sig.: %s\n", yes_no(hr->signed_locally));
488
489 if (hr->stop_delay_usec != UINT64_MAX) {
490 char buf[FORMAT_TIMESPAN_MAX];
491 printf(" Stop Delay: %s\n", format_timespan(buf, sizeof(buf), hr->stop_delay_usec, 0));
492 }
493
494 if (hr->auto_login >= 0)
495 printf("Autom. Login: %s\n", yes_no(hr->auto_login));
496
497 if (hr->kill_processes >= 0)
498 printf(" Kill Proc.: %s\n", yes_no(hr->kill_processes));
499
500 if (hr->service)
501 printf(" Service: %s\n", hr->service);
502 }