Serenity Operating System
1/*
2 * Copyright (c) 2020, Peter Elliott <pelliott@serenityos.org>
3 * Copyright (c) 2021, Andreas Kling <kling@serenityos.org>
4 *
5 * SPDX-License-Identifier: BSD-2-Clause
6 */
7
8#include <AK/ScopeGuard.h>
9#include <LibCore/Account.h>
10#include <LibCore/ArgsParser.h>
11#include <LibCore/GetPassword.h>
12#include <LibCore/System.h>
13#include <LibMain/Main.h>
14#include <string.h>
15#include <unistd.h>
16
17ErrorOr<int> serenity_main(Main::Arguments arguments)
18{
19 if (geteuid() != 0) {
20 warnln("Not running as root :^(");
21 return 1;
22 }
23
24 TRY(Core::System::setegid(0));
25
26 TRY(Core::System::pledge("stdio wpath rpath cpath fattr tty"));
27 TRY(Core::System::unveil("/etc", "rwc"));
28 TRY(Core::System::unveil(nullptr, nullptr));
29
30 bool del = false;
31 bool lock = false;
32 bool unlock = false;
33 StringView username {};
34
35 auto args_parser = Core::ArgsParser();
36 args_parser.set_general_help("Modify an account password.");
37 args_parser.add_option(del, "Delete password", "delete", 'd');
38 args_parser.add_option(lock, "Lock password", "lock", 'l');
39 args_parser.add_option(unlock, "Unlock password", "unlock", 'u');
40 args_parser.add_positional_argument(username, "Username", "username", Core::ArgsParser::Required::No);
41
42 args_parser.parse(arguments);
43
44 uid_t current_uid = getuid();
45
46 // target_account is the account we are changing the password of.
47 auto target_account = TRY(!username.is_empty()
48 ? Core::Account::from_name(username)
49 : Core::Account::from_uid(current_uid));
50
51 setpwent();
52
53 if (current_uid != 0 && current_uid != target_account.uid()) {
54 warnln("You can't modify passwd for {}", username);
55 return 1;
56 }
57
58 if (del) {
59 target_account.delete_password();
60 } else if (lock) {
61 target_account.set_password_enabled(false);
62 } else if (unlock) {
63 target_account.set_password_enabled(true);
64 } else {
65 if (current_uid != 0) {
66 auto current_password = TRY(Core::get_password("Current password: "sv));
67
68 if (!target_account.authenticate(current_password)) {
69 warnln("Incorrect or disabled password.");
70 warnln("Password for user {} unchanged.", target_account.username());
71 return 1;
72 }
73 }
74
75 auto new_password = TRY(Core::get_password("New password: "sv));
76 auto new_password_retype = TRY(Core::get_password("Retype new password: "sv));
77
78 if (new_password.is_empty() && new_password_retype.is_empty()) {
79 warnln("No password supplied.");
80 warnln("Password for user {} unchanged.", target_account.username());
81 return 1;
82 }
83
84 if (new_password.view() != new_password_retype.view()) {
85 warnln("Sorry, passwords don't match.");
86 warnln("Password for user {} unchanged.", target_account.username());
87 return 1;
88 }
89
90 target_account.set_password(new_password);
91 }
92
93 TRY(Core::System::pledge("stdio wpath rpath cpath fattr"));
94
95 TRY(target_account.sync());
96
97 outln("Password for user {} successfully updated.", target_account.username());
98 return 0;
99}