]> git.ipfire.org Git - thirdparty/systemd.git/blob - src/ask-password/ask-password.c
a100679af211f58e37488d6717ba038fc1badc16
[thirdparty/systemd.git] / src / ask-password / ask-password.c
1 /* SPDX-License-Identifier: LGPL-2.1-or-later */
2
3 #include <errno.h>
4 #include <getopt.h>
5 #include <stddef.h>
6 #include <unistd.h>
7
8 #include "ask-password-api.h"
9 #include "def.h"
10 #include "log.h"
11 #include "macro.h"
12 #include "main-func.h"
13 #include "parse-argument.h"
14 #include "pretty-print.h"
15 #include "strv.h"
16 #include "terminal-util.h"
17
18 static const char *arg_icon = NULL;
19 static const char *arg_id = NULL; /* identifier for 'ask-password' protocol */
20 static const char *arg_key_name = NULL; /* name in kernel keyring */
21 static const char *arg_credential_name = NULL; /* name in $CREDENTIALS_DIRECTORY directory */
22 static char *arg_message = NULL;
23 static usec_t arg_timeout = DEFAULT_TIMEOUT_USEC;
24 static bool arg_multiple = false;
25 static bool arg_no_output = false;
26 static AskPasswordFlags arg_flags = ASK_PASSWORD_PUSH_CACHE;
27 static bool arg_newline = true;
28
29 STATIC_DESTRUCTOR_REGISTER(arg_message, freep);
30
31 static int help(void) {
32 _cleanup_free_ char *link = NULL;
33 int r;
34
35 r = terminal_urlify_man("systemd-ask-password", "1", &link);
36 if (r < 0)
37 return log_oom();
38
39 printf("%1$s [OPTIONS...] MESSAGE\n\n"
40 "%3$sQuery the user for a system passphrase, via the TTY or an UI agent.%4$s\n\n"
41 " -h --help Show this help\n"
42 " --icon=NAME Icon name\n"
43 " --id=ID Query identifier (e.g. \"cryptsetup:/dev/sda5\")\n"
44 " --keyname=NAME Kernel key name for caching passwords (e.g. \"cryptsetup\")\n"
45 " --credential=NAME\n"
46 " Credential name for LoadCredential=/SetCredential=\n"
47 " credentials\n"
48 " --timeout=SEC Timeout in seconds\n"
49 " --echo=yes|no|masked\n"
50 " Control whether to show password while typing (echo)\n"
51 " -e --echo Equivalent to --echo=yes\n"
52 " --emoji=yes|no|auto\n"
53 " Show a lock and key emoji\n"
54 " --no-tty Ask question via agent even on TTY\n"
55 " --accept-cached Accept cached passwords\n"
56 " --multiple List multiple passwords if available\n"
57 " --no-output Do not print password to standard output\n"
58 " -n Do not suffix password written to standard output with\n"
59 " newline\n"
60 "\nSee the %2$s for details.\n",
61 program_invocation_short_name,
62 link,
63 ansi_highlight(),
64 ansi_normal());
65
66 return 0;
67 }
68
69 static int parse_argv(int argc, char *argv[]) {
70
71 enum {
72 ARG_ICON = 0x100,
73 ARG_TIMEOUT,
74 ARG_EMOJI,
75 ARG_NO_TTY,
76 ARG_ACCEPT_CACHED,
77 ARG_MULTIPLE,
78 ARG_ID,
79 ARG_KEYNAME,
80 ARG_NO_OUTPUT,
81 ARG_VERSION,
82 ARG_CREDENTIAL,
83 };
84
85 static const struct option options[] = {
86 { "help", no_argument, NULL, 'h' },
87 { "version", no_argument, NULL, ARG_VERSION },
88 { "icon", required_argument, NULL, ARG_ICON },
89 { "timeout", required_argument, NULL, ARG_TIMEOUT },
90 { "echo", optional_argument, NULL, 'e' },
91 { "emoji", required_argument, NULL, ARG_EMOJI },
92 { "no-tty", no_argument, NULL, ARG_NO_TTY },
93 { "accept-cached", no_argument, NULL, ARG_ACCEPT_CACHED },
94 { "multiple", no_argument, NULL, ARG_MULTIPLE },
95 { "id", required_argument, NULL, ARG_ID },
96 { "keyname", required_argument, NULL, ARG_KEYNAME },
97 { "no-output", no_argument, NULL, ARG_NO_OUTPUT },
98 { "credential", required_argument, NULL, ARG_CREDENTIAL },
99 {}
100 };
101
102 const char *emoji = NULL;
103 int c, r;
104
105 assert(argc >= 0);
106 assert(argv);
107
108 /* Note the asymmetry: the long option --echo= allows an optional argument, the short option does
109 * not. */
110 while ((c = getopt_long(argc, argv, "+hen", options, NULL)) >= 0)
111
112 switch (c) {
113
114 case 'h':
115 return help();
116
117 case ARG_VERSION:
118 return version();
119
120 case ARG_ICON:
121 arg_icon = optarg;
122 break;
123
124 case ARG_TIMEOUT:
125 r = parse_sec(optarg, &arg_timeout);
126 if (r < 0)
127 return log_error_errno(r, "Failed to parse --timeout= parameter: %s", optarg);
128
129 break;
130
131 case 'e':
132 if (!optarg) {
133 /* Short option -e is used, or no argument to long option --echo= */
134 arg_flags |= ASK_PASSWORD_ECHO;
135 arg_flags &= ~ASK_PASSWORD_SILENT;
136 } else if (isempty(optarg) || streq(optarg, "masked"))
137 /* Empty argument or explicit string "masked" for default behaviour. */
138 arg_flags &= ~(ASK_PASSWORD_ECHO|ASK_PASSWORD_SILENT);
139 else {
140 bool b;
141
142 r = parse_boolean_argument("--echo=", optarg, &b);
143 if (r < 0)
144 return r;
145
146 SET_FLAG(arg_flags, ASK_PASSWORD_ECHO, b);
147 SET_FLAG(arg_flags, ASK_PASSWORD_SILENT, !b);
148 }
149 break;
150
151 case ARG_EMOJI:
152 emoji = optarg;
153 break;
154
155 case ARG_NO_TTY:
156 arg_flags |= ASK_PASSWORD_NO_TTY;
157 break;
158
159 case ARG_ACCEPT_CACHED:
160 arg_flags |= ASK_PASSWORD_ACCEPT_CACHED;
161 break;
162
163 case ARG_MULTIPLE:
164 arg_multiple = true;
165 break;
166
167 case ARG_ID:
168 arg_id = optarg;
169 break;
170
171 case ARG_KEYNAME:
172 arg_key_name = optarg;
173 break;
174
175 case ARG_NO_OUTPUT:
176 arg_no_output = true;
177 break;
178
179 case ARG_CREDENTIAL:
180 arg_credential_name = optarg;
181 break;
182
183 case 'n':
184 arg_newline = false;
185 break;
186
187 case '?':
188 return -EINVAL;
189
190 default:
191 assert_not_reached();
192 }
193
194 if (isempty(emoji) || streq(emoji, "auto"))
195 SET_FLAG(arg_flags, ASK_PASSWORD_HIDE_EMOJI, FLAGS_SET(arg_flags, ASK_PASSWORD_ECHO));
196 else {
197 bool b;
198
199 r = parse_boolean_argument("--emoji=", emoji, &b);
200 if (r < 0)
201 return r;
202
203 SET_FLAG(arg_flags, ASK_PASSWORD_HIDE_EMOJI, !b);
204 }
205
206 if (argc > optind) {
207 arg_message = strv_join(argv + optind, " ");
208 if (!arg_message)
209 return log_oom();
210 } else if (FLAGS_SET(arg_flags, ASK_PASSWORD_ECHO)) {
211 /* By default ask_password_auto() will query with the string "Password: ", which is not right
212 * when full echo is on, since then it's unlikely a password. Let's hence default to a less
213 * confusing string in that case. */
214
215 arg_message = strdup("Input:");
216 if (!arg_message)
217 return log_oom();
218 }
219
220 return 1;
221 }
222
223 static int run(int argc, char *argv[]) {
224 _cleanup_strv_free_erase_ char **l = NULL;
225 usec_t timeout;
226 char **p;
227 int r;
228
229 log_show_color(true);
230 log_parse_environment();
231 log_open();
232
233 r = parse_argv(argc, argv);
234 if (r <= 0)
235 return r;
236
237 if (arg_timeout > 0)
238 timeout = usec_add(now(CLOCK_MONOTONIC), arg_timeout);
239 else
240 timeout = 0;
241
242 r = ask_password_auto(arg_message, arg_icon, arg_id, arg_key_name, arg_credential_name ?: "password", timeout, arg_flags, &l);
243 if (r < 0)
244 return log_error_errno(r, "Failed to query password: %m");
245
246 STRV_FOREACH(p, l) {
247 if (!arg_no_output) {
248 if (arg_newline)
249 puts(*p);
250 else
251 fputs(*p, stdout);
252 }
253
254 fflush(stdout);
255
256 if (!arg_multiple)
257 break;
258 }
259
260 return 0;
261 }
262
263 DEFINE_MAIN_FUNCTION(run);