]> git.ipfire.org Git - thirdparty/systemd.git/commitdiff
shared: add helpers for displaying new-style user/group records to users
authorLennart Poettering <lennart@poettering.net>
Mon, 5 Aug 2019 16:21:49 +0000 (18:21 +0200)
committerLennart Poettering <lennart@poettering.net>
Wed, 15 Jan 2020 14:27:59 +0000 (15:27 +0100)
src/shared/group-record-show.c [new file with mode: 0644]
src/shared/group-record-show.h [new file with mode: 0644]
src/shared/meson.build
src/shared/user-record-show.c [new file with mode: 0644]
src/shared/user-record-show.h [new file with mode: 0644]

diff --git a/src/shared/group-record-show.c b/src/shared/group-record-show.c
new file mode 100644 (file)
index 0000000..d0300e4
--- /dev/null
@@ -0,0 +1,76 @@
+/* SPDX-License-Identifier: LGPL-2.1+ */
+
+#include "format-util.h"
+#include "group-record-show.h"
+#include "strv.h"
+#include "user-util.h"
+#include "userdb.h"
+
+void group_record_show(GroupRecord *gr, bool show_full_user_info) {
+        int r;
+
+        printf("  Group name: %s\n",
+               group_record_group_name_and_realm(gr));
+
+        printf(" Disposition: %s\n", user_disposition_to_string(group_record_disposition(gr)));
+
+        if (gr->last_change_usec != USEC_INFINITY) {
+                char buf[FORMAT_TIMESTAMP_MAX];
+                printf(" Last Change: %s\n", format_timestamp(buf, sizeof(buf), gr->last_change_usec));
+        }
+
+        if (gid_is_valid(gr->gid))
+                printf("         GID: " GID_FMT "\n", gr->gid);
+
+        if (show_full_user_info) {
+                _cleanup_(userdb_iterator_freep) UserDBIterator *iterator = NULL;
+
+                r = membershipdb_by_group(gr->group_name, 0, &iterator);
+                if (r < 0) {
+                        errno = -r;
+                        printf("     Members: (can't acquire: %m)");
+                } else {
+                        const char *prefix = "     Members:";
+
+                        for (;;) {
+                                _cleanup_free_ char *user = NULL;
+
+                                r = membershipdb_iterator_get(iterator, &user, NULL);
+                                if (r == -ESRCH)
+                                        break;
+                                if (r < 0) {
+                                        errno = -r;
+                                        printf("%s (can't iterate: %m\n", prefix);
+                                        break;
+                                }
+
+                                printf("%s %s\n", prefix, user);
+                                prefix = "             ";
+                        }
+                }
+        } else {
+                const char *prefix = "     Members:";
+                char **i;
+
+                STRV_FOREACH(i, gr->members) {
+                        printf("%s %s\n", prefix, *i);
+                        prefix = "             ";
+                }
+        }
+
+        if (!strv_isempty(gr->administrators)) {
+                const char *prefix = "      Admins:";
+                char **i;
+
+                STRV_FOREACH(i, gr->administrators) {
+                        printf("%s %s\n", prefix, *i);
+                        prefix = "             ";
+                }
+        }
+
+        if (!strv_isempty(gr->hashed_password))
+                printf("   Passwords: %zu\n", strv_length(gr->hashed_password));
+
+        if (gr->service)
+                printf("     Service: %s\n", gr->service);
+}
diff --git a/src/shared/group-record-show.h b/src/shared/group-record-show.h
new file mode 100644 (file)
index 0000000..12bdbd1
--- /dev/null
@@ -0,0 +1,6 @@
+/* SPDX-License-Identifier: LGPL-2.1+ */
+#pragma once
+
+#include "group-record.h"
+
+void group_record_show(GroupRecord *gr, bool show_full_user_info);
index aaa0bcf794863f3104b10f00594c6206ba144a8a..4d98fb2c178e00540912033d0de4b62263359c52 100644 (file)
@@ -88,6 +88,8 @@ shared_sources = files('''
         gpt.h
         group-record-nss.c
         group-record-nss.h
+        group-record-show.c
+        group-record-show.h
         group-record.c
         group-record.h
         id128-print.c
@@ -195,6 +197,8 @@ shared_sources = files('''
         unit-file.h
         user-record-nss.c
         user-record-nss.h
+        user-record-show.c
+        user-record-show.h
         user-record.c
         user-record.h
         userdb.c
diff --git a/src/shared/user-record-show.c b/src/shared/user-record-show.c
new file mode 100644 (file)
index 0000000..e5eff50
--- /dev/null
@@ -0,0 +1,466 @@
+/* SPDX-License-Identifier: LGPL-2.1+ */
+
+#include "format-util.h"
+#include "fs-util.h"
+#include "group-record.h"
+#include "process-util.h"
+#include "rlimit-util.h"
+#include "strv.h"
+#include "terminal-util.h"
+#include "user-record-show.h"
+#include "user-util.h"
+#include "userdb.h"
+
+const char *user_record_state_color(const char *state) {
+        if (STR_IN_SET(state, "unfixated", "absent"))
+                return ansi_grey();
+        else if (streq(state, "active"))
+                return ansi_highlight_green();
+        else if (streq(state, "locked"))
+                 return ansi_highlight_yellow();
+
+        return NULL;
+}
+
+void user_record_show(UserRecord *hr, bool show_full_group_info) {
+        const char *hd, *ip, *shell;
+        UserStorage storage;
+        usec_t t;
+        size_t k;
+        int r, b;
+
+        printf("   User name: %s\n",
+               user_record_user_name_and_realm(hr));
+
+        if (hr->state) {
+                const char *color;
+
+                color = user_record_state_color(hr->state);
+
+                printf("       State: %s%s%s\n",
+                       strempty(color), hr->state, color ? ansi_normal() : "");
+        }
+
+        printf(" Disposition: %s\n", user_disposition_to_string(user_record_disposition(hr)));
+
+        if (hr->last_change_usec != USEC_INFINITY) {
+                char buf[FORMAT_TIMESTAMP_MAX];
+                printf(" Last Change: %s\n", format_timestamp(buf, sizeof(buf), hr->last_change_usec));
+        }
+
+        if (hr->last_password_change_usec != USEC_INFINITY &&
+            hr->last_password_change_usec != hr->last_change_usec) {
+                char buf[FORMAT_TIMESTAMP_MAX];
+                printf(" Last Passw.: %s\n", format_timestamp(buf, sizeof(buf), hr->last_password_change_usec));
+        }
+
+        r = user_record_test_blocked(hr);
+        switch (r) {
+
+        case -ESTALE:
+                printf("    Login OK: %sno%s (last change time is in the future)\n", ansi_highlight_red(), ansi_normal());
+                break;
+
+        case -ENOLCK:
+                printf("    Login OK: %sno%s (record is locked)\n", ansi_highlight_red(), ansi_normal());
+                break;
+
+        case -EL2HLT:
+                printf("    Login OK: %sno%s (record not valid yet))\n", ansi_highlight_red(), ansi_normal());
+                break;
+
+        case -EL3HLT:
+                printf("    Login OK: %sno%s (record not valid anymore))\n", ansi_highlight_red(), ansi_normal());
+                break;
+
+        default: {
+                usec_t y;
+
+                if (r < 0) {
+                        errno = -r;
+                        printf("    Login OK: %sno%s (%m)\n", ansi_highlight_red(), ansi_normal());
+                        break;
+                }
+
+                if (is_nologin_shell(user_record_shell(hr))) {
+                        printf("    Login OK: %sno%s (nologin shell)\n", ansi_highlight_red(), ansi_normal());
+                        break;
+                }
+
+                y = user_record_ratelimit_next_try(hr);
+                if (y != USEC_INFINITY && y > now(CLOCK_REALTIME)) {
+                        printf("    Login OK: %sno%s (ratelimit)\n", ansi_highlight_red(), ansi_normal());
+                        break;
+                }
+
+                printf("    Login OK: %syes%s\n", ansi_highlight_green(), ansi_normal());
+                break;
+        }}
+
+        r = user_record_test_password_change_required(hr);
+        switch (r) {
+
+        case -EKEYREVOKED:
+                printf(" Password OK: %schange now%s\n", ansi_highlight_yellow(), ansi_normal());
+                break;
+
+        case -EOWNERDEAD:
+                printf(" Password OK: %sexpired%s (change now!)\n", ansi_highlight_yellow(), ansi_normal());
+                break;
+
+        case -EKEYREJECTED:
+                printf(" Password OK: %sexpired%s (for good)\n", ansi_highlight_red(), ansi_normal());
+                break;
+
+        case -EKEYEXPIRED:
+                printf(" Password OK: %sexpires soon%s\n", ansi_highlight_yellow(), ansi_normal());
+                break;
+
+        case -ENETDOWN:
+                printf(" Password OK: %sno timestamp%s\n", ansi_highlight_red(), ansi_normal());
+                break;
+
+        case -EROFS:
+                printf(" Password OK: %schange not permitted%s\n", ansi_highlight_yellow(), ansi_normal());
+                break;
+
+        default:
+                if (r < 0) {
+                        errno = -r;
+                        printf(" Password OK: %sno%s (%m)\n", ansi_highlight_yellow(), ansi_normal());
+                        break;
+                }
+
+                printf(" Password OK: %syes%s\n", ansi_highlight_green(), ansi_normal());
+                break;
+        }
+
+        if (uid_is_valid(hr->uid))
+                printf("         UID: " UID_FMT "\n", hr->uid);
+        if (gid_is_valid(hr->gid)) {
+                if (show_full_group_info) {
+                        _cleanup_(group_record_unrefp) GroupRecord *gr = NULL;
+
+                        r = groupdb_by_gid(hr->gid, 0, &gr);
+                        if (r < 0) {
+                                errno = -r;
+                                printf("         GID: " GID_FMT " (unresolvable: %m)\n", hr->gid);
+                        } else
+                                printf("         GID: " GID_FMT " (%s)\n", hr->gid, gr->group_name);
+                } else
+                        printf("         GID: " GID_FMT "\n", hr->gid);
+        } else if (uid_is_valid(hr->uid)) /* Show UID as GID if not separately configured */
+                printf("         GID: " GID_FMT "\n", (gid_t) hr->uid);
+
+        if (show_full_group_info) {
+                _cleanup_(userdb_iterator_freep) UserDBIterator *iterator = NULL;
+
+                r = membershipdb_by_user(hr->user_name, 0, &iterator);
+                if (r < 0) {
+                        errno = -r;
+                        printf(" Aux. Groups: (can't acquire: %m)\n");
+                } else {
+                        const char *prefix = " Aux. Groups:";
+
+                        for (;;) {
+                                _cleanup_free_ char *group = NULL;
+
+                                r = membershipdb_iterator_get(iterator, NULL, &group);
+                                if (r == -ESRCH)
+                                        break;
+                                if (r < 0) {
+                                        errno = -r;
+                                        printf("%s (can't iterate: %m)\n", prefix);
+                                        break;
+                                }
+
+                                printf("%s %s\n", prefix, group);
+                                prefix = "             ";
+                        }
+                }
+        }
+
+        if (hr->real_name && !streq(hr->real_name, hr->user_name))
+                printf("   Real Name: %s\n", hr->real_name);
+
+        hd = user_record_home_directory(hr);
+        if (hd)
+                printf("   Directory: %s\n", hd);
+
+        storage = user_record_storage(hr);
+        if (storage >= 0) /* Let's be political, and clarify which storage we like, and which we don't. About CIFS we don't complain. */
+                printf("     Storage: %s%s\n", user_storage_to_string(storage),
+                       storage == USER_LUKS ? " (strong encryption)" :
+                       storage == USER_FSCRYPT ? " (weak encryption)" :
+                       IN_SET(storage, USER_DIRECTORY, USER_SUBVOLUME) ? " (no encryption)" : "");
+
+        ip = user_record_image_path(hr);
+        if (ip && !streq_ptr(ip, hd))
+                printf("  Image Path: %s\n", ip);
+
+        b = user_record_removable(hr);
+        if (b >= 0)
+                printf("   Removable: %s\n", yes_no(b));
+
+        shell = user_record_shell(hr);
+        if (shell)
+                printf("       Shell: %s\n", shell);
+
+        if (hr->email_address)
+                printf("       Email: %s\n", hr->email_address);
+        if (hr->location)
+                printf("    Location: %s\n", hr->location);
+        if (hr->password_hint)
+                printf(" Passw. Hint: %s\n", hr->password_hint);
+        if (hr->icon_name)
+                printf("   Icon Name: %s\n", hr->icon_name);
+
+        if (hr->time_zone)
+                printf("   Time Zone: %s\n", hr->time_zone);
+
+        if (hr->preferred_language)
+                printf("    Language: %s\n", hr->preferred_language);
+
+        if (!strv_isempty(hr->environment)) {
+                char **i;
+
+                STRV_FOREACH(i, hr->environment) {
+                        printf(i == hr->environment ?
+                               " Environment: %s\n" :
+                               "              %s\n", *i);
+                }
+        }
+
+        if (hr->locked >= 0)
+                printf("      Locked: %s\n", yes_no(hr->locked));
+
+        if (hr->not_before_usec != UINT64_MAX) {
+                char buf[FORMAT_TIMESTAMP_MAX];
+                printf("  Not Before: %s\n", format_timestamp(buf, sizeof(buf), hr->not_before_usec));
+        }
+
+        if (hr->not_after_usec != UINT64_MAX) {
+                char buf[FORMAT_TIMESTAMP_MAX];
+                printf("   Not After: %s\n", format_timestamp(buf, sizeof(buf), hr->not_after_usec));
+        }
+
+        if (hr->umask != MODE_INVALID)
+                printf("       UMask: 0%03o\n", hr->umask);
+
+        if (nice_is_valid(hr->nice_level))
+                printf("        Nice: %i\n", hr->nice_level);
+
+        for (int j = 0; j < _RLIMIT_MAX; j++) {
+                if (hr->rlimits[j])
+                        printf("       Limit: RLIMIT_%s=%" PRIu64 ":%" PRIu64 "\n",
+                               rlimit_to_string(j), (uint64_t) hr->rlimits[j]->rlim_cur, (uint64_t) hr->rlimits[j]->rlim_max);
+        }
+
+        if (hr->tasks_max != UINT64_MAX)
+                printf("   Tasks Max: %" PRIu64 "\n", hr->tasks_max);
+
+        if (hr->memory_high != UINT64_MAX) {
+                char buf[FORMAT_BYTES_MAX];
+                printf(" Memory High: %s\n", format_bytes(buf, sizeof(buf), hr->memory_high));
+        }
+
+        if (hr->memory_max != UINT64_MAX) {
+                char buf[FORMAT_BYTES_MAX];
+                printf("  Memory Max: %s\n", format_bytes(buf, sizeof(buf), hr->memory_max));
+        }
+
+        if (hr->cpu_weight != UINT64_MAX)
+                printf("  CPU Weight: %" PRIu64 "\n", hr->cpu_weight);
+
+        if (hr->io_weight != UINT64_MAX)
+                printf("   IO Weight: %" PRIu64 "\n", hr->io_weight);
+
+        if (hr->access_mode != MODE_INVALID)
+                printf(" Access Mode: 0%03oo\n", user_record_access_mode(hr));
+
+        if (storage == USER_LUKS) {
+                printf("LUKS Discard: %s\n", yes_no(user_record_luks_discard(hr)));
+
+                if (!sd_id128_is_null(hr->luks_uuid))
+                        printf("   LUKS UUID: " SD_ID128_FORMAT_STR "\n", SD_ID128_FORMAT_VAL(hr->luks_uuid));
+                if (!sd_id128_is_null(hr->partition_uuid))
+                        printf("   Part UUID: " SD_ID128_FORMAT_STR "\n", SD_ID128_FORMAT_VAL(hr->partition_uuid));
+                if (!sd_id128_is_null(hr->file_system_uuid))
+                        printf("     FS UUID: " SD_ID128_FORMAT_STR "\n", SD_ID128_FORMAT_VAL(hr->file_system_uuid));
+
+                if (hr->file_system_type)
+                        printf(" File System: %s\n", user_record_file_system_type(hr));
+
+                if (hr->luks_cipher)
+                        printf(" LUKS Cipher: %s\n", hr->luks_cipher);
+                if (hr->luks_cipher_mode)
+                        printf(" Cipher Mode: %s\n", hr->luks_cipher_mode);
+                if (hr->luks_volume_key_size != UINT64_MAX)
+                        printf("  Volume Key: %" PRIu64 "bit\n", hr->luks_volume_key_size * 8);
+
+                if (hr->luks_pbkdf_type)
+                        printf("  PBKDF Type: %s\n", hr->luks_pbkdf_type);
+                if (hr->luks_pbkdf_hash_algorithm)
+                        printf("  PBKDF Hash: %s\n", hr->luks_pbkdf_hash_algorithm);
+                if (hr->luks_pbkdf_time_cost_usec != UINT64_MAX) {
+                        char buf[FORMAT_TIMESPAN_MAX];
+                        printf("  PBKDF Time: %s\n", format_timespan(buf, sizeof(buf), hr->luks_pbkdf_time_cost_usec, 0));
+                }
+                if (hr->luks_pbkdf_memory_cost != UINT64_MAX) {
+                        char buf[FORMAT_BYTES_MAX];
+                        printf(" PBKDF Bytes: %s\n", format_bytes(buf, sizeof(buf), hr->luks_pbkdf_memory_cost));
+                }
+                if (hr->luks_pbkdf_parallel_threads != UINT64_MAX)
+                        printf("PBKDF Thread: %" PRIu64 "\n", hr->luks_pbkdf_parallel_threads);
+
+        } else if (storage == USER_CIFS) {
+
+                if (hr->cifs_service)
+                        printf("CIFS Service: %s\n", hr->cifs_service);
+        }
+
+        if (hr->cifs_user_name)
+                printf("   CIFS User: %s\n", user_record_cifs_user_name(hr));
+        if (hr->cifs_domain)
+                printf(" CIFS Domain: %s\n", hr->cifs_domain);
+
+        if (storage != USER_CLASSIC)
+                printf(" Mount Flags: %s %s %s\n",
+                       hr->nosuid ? "nosuid" : "suid",
+                       hr->nodev ? "nodev" : "dev",
+                       hr->noexec ? "noexec" : "exec");
+
+        if (hr->skeleton_directory)
+                printf("  Skel. Dir.: %s\n", user_record_skeleton_directory(hr));
+
+        if (hr->disk_size != UINT64_MAX) {
+                char buf[FORMAT_BYTES_MAX];
+                printf("   Disk Size: %s\n", format_bytes(buf, sizeof(buf), hr->disk_size));
+        }
+
+        if (hr->disk_usage != UINT64_MAX) {
+                char buf[FORMAT_BYTES_MAX];
+                printf("  Disk Usage: %s\n", format_bytes(buf, sizeof(buf), hr->disk_usage));
+        }
+
+        if (hr->disk_free != UINT64_MAX) {
+                char buf[FORMAT_BYTES_MAX];
+                printf("   Disk Free: %s\n", format_bytes(buf, sizeof(buf), hr->disk_free));
+        }
+
+        if (hr->disk_floor != UINT64_MAX) {
+                char buf[FORMAT_BYTES_MAX];
+                printf("  Disk Floor: %s\n", format_bytes(buf, sizeof(buf), hr->disk_floor));
+        }
+
+        if (hr->disk_ceiling != UINT64_MAX) {
+                char buf[FORMAT_BYTES_MAX];
+                printf("Disk Ceiling: %s\n", format_bytes(buf, sizeof(buf), hr->disk_ceiling));
+        }
+
+        if (hr->good_authentication_counter != UINT64_MAX)
+                printf("  Good Auth.: %" PRIu64 "\n", hr->good_authentication_counter);
+
+        if (hr->last_good_authentication_usec != UINT64_MAX) {
+                char buf[FORMAT_TIMESTAMP_MAX];
+                printf("   Last Good: %s\n", format_timestamp(buf, sizeof(buf), hr->last_good_authentication_usec));
+        }
+
+        if (hr->bad_authentication_counter != UINT64_MAX)
+                printf("   Bad Auth.: %" PRIu64 "\n", hr->bad_authentication_counter);
+
+        if (hr->last_bad_authentication_usec != UINT64_MAX) {
+                char buf[FORMAT_TIMESTAMP_MAX];
+                printf("    Last Bad: %s\n", format_timestamp(buf, sizeof(buf), hr->last_bad_authentication_usec));
+        }
+
+        t = user_record_ratelimit_next_try(hr);
+        if (t != USEC_INFINITY) {
+                usec_t n = now(CLOCK_REALTIME);
+
+                if (t <= n)
+                        printf("    Next Try: anytime\n");
+                else {
+                        char buf[FORMAT_TIMESPAN_MAX];
+                        printf("    Next Try: %sin %s%s\n",
+                               ansi_highlight_red(),
+                               format_timespan(buf, sizeof(buf), t - n, USEC_PER_SEC),
+                               ansi_normal());
+                }
+        }
+
+        if (storage != USER_CLASSIC) {
+                char buf[FORMAT_TIMESPAN_MAX];
+                printf(" Auth. Limit: %" PRIu64 " attempts per %s\n", user_record_ratelimit_burst(hr),
+                       format_timespan(buf, sizeof(buf), user_record_ratelimit_interval_usec(hr), 0));
+        }
+
+        if (hr->enforce_password_policy >= 0)
+                printf(" Passwd Pol.: %s\n", yes_no(hr->enforce_password_policy));
+
+        if (hr->password_change_min_usec != UINT64_MAX ||
+            hr->password_change_max_usec != UINT64_MAX ||
+            hr->password_change_warn_usec != UINT64_MAX ||
+            hr->password_change_inactive_usec != UINT64_MAX) {
+
+                char buf[FORMAT_TIMESPAN_MAX];
+                printf(" Passwd Chg.:");
+
+                if (hr->password_change_min_usec != UINT64_MAX) {
+                        printf(" min %s", format_timespan(buf, sizeof(buf), hr->password_change_min_usec, 0));
+
+                        if (hr->password_change_max_usec != UINT64_MAX)
+                                printf(" …");
+                }
+
+                if (hr->password_change_max_usec != UINT64_MAX)
+                        printf(" max %s", format_timespan(buf, sizeof(buf), hr->password_change_max_usec, 0));
+
+                if (hr->password_change_warn_usec != UINT64_MAX)
+                        printf("/warn %s", format_timespan(buf, sizeof(buf), hr->password_change_warn_usec, 0));
+
+                if (hr->password_change_inactive_usec != UINT64_MAX)
+                        printf("/inactive %s", format_timespan(buf, sizeof(buf), hr->password_change_inactive_usec, 0));
+
+                printf("\n");
+        }
+
+        if (hr->password_change_now >= 0)
+                printf("Pas. Ch. Now: %s\n", yes_no(hr->password_change_now));
+
+        if (!strv_isempty(hr->ssh_authorized_keys))
+                printf("SSH Pub. Key: %zu\n", strv_length(hr->ssh_authorized_keys));
+
+        if (!strv_isempty(hr->pkcs11_token_uri)) {
+                char **i;
+
+                STRV_FOREACH(i, hr->pkcs11_token_uri)
+                        printf(i == hr->pkcs11_token_uri ?
+                               "  Sec. Token: %s\n" :
+                               "              %s\n", *i);
+        }
+
+        k = strv_length(hr->hashed_password);
+        if (k == 0)
+                printf("   Passwords: %snone%s\n",
+                       user_record_disposition(hr) == USER_REGULAR ? ansi_highlight_yellow() : ansi_normal(), ansi_normal());
+        else
+                printf("   Passwords: %zu\n", k);
+
+        if (hr->signed_locally >= 0)
+                printf("  Local Sig.: %s\n", yes_no(hr->signed_locally));
+
+        if (hr->stop_delay_usec != UINT64_MAX) {
+                char buf[FORMAT_TIMESPAN_MAX];
+                printf("  Stop Delay: %s\n", format_timespan(buf, sizeof(buf), hr->stop_delay_usec, 0));
+        }
+
+        if (hr->auto_login >= 0)
+                printf("Autom. Login: %s\n", yes_no(hr->auto_login));
+
+        if (hr->kill_processes >= 0)
+                printf("  Kill Proc.: %s\n", yes_no(hr->kill_processes));
+
+        if (hr->service)
+                printf("     Service: %s\n", hr->service);
+}
diff --git a/src/shared/user-record-show.h b/src/shared/user-record-show.h
new file mode 100644 (file)
index 0000000..bd22be2
--- /dev/null
@@ -0,0 +1,8 @@
+/* SPDX-License-Identifier: LGPL-2.1+ */
+#pragma once
+
+#include "user-record.h"
+
+const char *user_record_state_color(const char *state);
+
+void user_record_show(UserRecord *hr, bool show_full_group_info);