pamu2fcfg.c revision 1.1.1.1 1 /*
2 * Copyright (C) 2014-2018 Yubico AB - See COPYING
3 */
4
5 #define BUFSIZE 1024
6 #define PAM_PREFIX "pam://"
7 #define TIMEOUT 15
8 #define FREQUENCY 1
9
10 #include <fido.h>
11
12 #include <stdio.h>
13 #include <stdlib.h>
14 #include <string.h>
15 #include <getopt.h>
16 #include <unistd.h>
17 #include <sys/types.h>
18 #include <pwd.h>
19
20 #include "b64.h"
21 #include "cmdline.h"
22 #include "util.h"
23 #ifndef HAVE_READPASSPHRASE
24 #include "_readpassphrase.h"
25 #else
26 #include <readpassphrase.h>
27 #endif
28
29 int main(int argc, char *argv[]) {
30 int exit_code = EXIT_FAILURE;
31 struct gengetopt_args_info args_info;
32 char buf[BUFSIZE];
33 char prompt[BUFSIZE];
34 char pin[BUFSIZE];
35 char *p;
36 char *response;
37 fido_cred_t *cred = NULL;
38 fido_dev_info_t *devlist = NULL;
39 fido_dev_t *dev = NULL;
40 const fido_dev_info_t *di = NULL;
41 size_t ndevs;
42 int cose_type;
43 int resident_key;
44 int user_presence;
45 int user_verification;
46 int pin_verification;
47 int r;
48 int n;
49 char *origin = NULL;
50 char *appid = NULL;
51 char *user = NULL;
52 char *b64_kh;
53 char *b64_pk;
54 struct passwd *passwd;
55 const unsigned char *kh = NULL;
56 size_t kh_len;
57 const unsigned char *pk = NULL;
58 size_t pk_len;
59 unsigned char userid[32];
60 unsigned char challenge[32];
61 unsigned i;
62 unsigned max_index = 0;
63
64 if (cmdline_parser(argc, argv, &args_info) != 0)
65 exit(EXIT_FAILURE);
66
67 if (args_info.help_given) {
68 cmdline_parser_print_help();
69 printf("\nReport bugs at <https://github.com/Yubico/pam-u2f>.\n");
70 exit(EXIT_SUCCESS);
71 }
72
73 fido_init(args_info.debug_flag ? FIDO_DEBUG : 0);
74
75 cred = fido_cred_new();
76 if (!cred) {
77 fprintf(stderr, "fido_cred_new failed\n");
78 exit(EXIT_FAILURE);
79 }
80
81 if (!random_bytes(challenge, sizeof(challenge))) {
82 fprintf(stderr, "random_bytes failed\n");
83 exit(EXIT_FAILURE);
84 }
85
86 if (args_info.type_given) {
87 if (!strcasecmp(args_info.type_arg, "es256"))
88 cose_type = COSE_ES256;
89 else if (!strcasecmp(args_info.type_arg, "rs256"))
90 cose_type = COSE_RS256;
91 else {
92 fprintf(stderr, "Unknown COSE type '%s'.\n", args_info.type_arg);
93 exit(EXIT_FAILURE);
94 }
95 } else
96 cose_type = COSE_ES256;
97
98 r = fido_cred_set_type(cred, cose_type);
99 if (r != FIDO_OK) {
100 fprintf(stderr, "error: fido_cred_set_type (%d): %s\n", r, fido_strerr(r));
101 exit(EXIT_FAILURE);
102 }
103
104 r = fido_cred_set_clientdata_hash(cred, challenge, sizeof(challenge));
105 if (r != FIDO_OK) {
106 fprintf(stderr, "error: fido_cred_set_clientdata_hash (%d): %s\n", r,
107 fido_strerr(r));
108 exit(EXIT_FAILURE);
109 }
110
111 if (args_info.origin_given)
112 origin = args_info.origin_arg;
113 else {
114 if (!strcpy(buf, PAM_PREFIX)) {
115 fprintf(stderr, "strcpy failed\n");
116 exit(EXIT_FAILURE);
117 }
118 if (gethostname(buf + strlen(PAM_PREFIX), BUFSIZE - strlen(PAM_PREFIX)) ==
119 -1) {
120 perror("gethostname");
121 exit(EXIT_FAILURE);
122 }
123 origin = buf;
124 }
125
126 if (args_info.verbose_given)
127 fprintf(stderr, "Setting origin to %s\n", origin);
128
129 if (args_info.appid_given)
130 appid = args_info.appid_arg;
131 else {
132 appid = origin;
133 }
134
135 if (args_info.verbose_given)
136 fprintf(stderr, "Setting appid to %s\n", appid);
137
138 r = fido_cred_set_rp(cred, origin, appid);
139 if (r != FIDO_OK) {
140 fprintf(stderr, "error: fido_cred_set_rp (%d) %s\n", r, fido_strerr(r));
141 exit(EXIT_FAILURE);
142 }
143
144 if (args_info.username_given)
145 user = args_info.username_arg;
146 else {
147 passwd = getpwuid(getuid());
148 if (passwd == NULL) {
149 perror("getpwuid");
150 exit(EXIT_FAILURE);
151 }
152 user = passwd->pw_name;
153 }
154
155 if (!random_bytes(userid, sizeof(userid))) {
156 fprintf(stderr, "random_bytes failed\n");
157 exit(EXIT_FAILURE);
158 }
159
160 if (args_info.verbose_given) {
161 fprintf(stderr, "Setting user to %s\n", user);
162 fprintf(stderr, "Setting user id to ");
163 for (size_t i = 0; i < sizeof(userid); i++)
164 fprintf(stderr, "%02x", userid[i]);
165 fprintf(stderr, "\n");
166 }
167
168 r = fido_cred_set_user(cred, userid, sizeof(userid), user, NULL, NULL);
169 if (r != FIDO_OK) {
170 fprintf(stderr, "error: fido_cred_set_user (%d) %s\n", r, fido_strerr(r));
171 exit(EXIT_FAILURE);
172 }
173
174 if (args_info.resident_given)
175 resident_key = 1;
176 else
177 resident_key = 0;
178
179 if (args_info.no_user_presence_given)
180 user_presence = 0;
181 else
182 user_presence = 1;
183
184 if (args_info.user_verification_given)
185 user_verification = 1;
186 else
187 user_verification = 0;
188
189 if (args_info.pin_verification_given)
190 pin_verification = 1;
191 else
192 pin_verification = 0;
193
194 r = fido_cred_set_rk(cred, resident_key);
195 if (r != FIDO_OK) {
196 fprintf(stderr, "error: fido_cred_set_rk (%d) %s\n", r, fido_strerr(r));
197 exit(EXIT_FAILURE);
198 }
199
200 r = fido_cred_set_uv(cred, false);
201 if (r != FIDO_OK) {
202 fprintf(stderr, "error: fido_cred_set_uv (%d) %s\n", r, fido_strerr(r));
203 exit(EXIT_FAILURE);
204 }
205
206 devlist = fido_dev_info_new(64);
207 if (!devlist) {
208 fprintf(stderr, "error: fido_dev_info_new failed\n");
209 exit(EXIT_FAILURE);
210 }
211
212 r = fido_dev_info_manifest(devlist, 64, &ndevs);
213 if (r != FIDO_OK) {
214 fprintf(stderr, "Unable to discover device(s), %s (%d)\n", fido_strerr(r),
215 r);
216 exit(EXIT_FAILURE);
217 }
218
219 if (ndevs == 0) {
220 for (i = 0; i < TIMEOUT; i += FREQUENCY) {
221 fprintf(stderr,
222 "\rNo U2F device available, please insert one now, you "
223 "have %2d seconds",
224 TIMEOUT - i);
225 fflush(stderr);
226 sleep(FREQUENCY);
227
228 r = fido_dev_info_manifest(devlist, 64, &ndevs);
229 if (r != FIDO_OK) {
230 fprintf(stderr, "\nUnable to discover device(s), %s (%d)",
231 fido_strerr(r), r);
232 exit(EXIT_FAILURE);
233 }
234
235 if (ndevs != 0) {
236 fprintf(stderr, "\nDevice found!\n");
237 break;
238 }
239 }
240 }
241
242 if (ndevs == 0) {
243 fprintf(stderr, "\rNo device found. Aborting. "
244 " \n");
245 exit(EXIT_FAILURE);
246 }
247
248 /* XXX loop over every device? */
249 dev = fido_dev_new();
250 if (!dev) {
251 fprintf(stderr, "fido_dev_new failed\n");
252 exit(EXIT_FAILURE);
253 }
254
255 di = fido_dev_info_ptr(devlist, 0);
256 if (!di) {
257 fprintf(stderr, "error: fido_dev_info_ptr returned NULL\n");
258 exit(EXIT_FAILURE);
259 }
260
261 r = fido_dev_open(dev, fido_dev_info_path(di));
262 if (r != FIDO_OK) {
263 fprintf(stderr, "error: fido_dev_open (%d) %s\n", r, fido_strerr(r));
264 exit(EXIT_FAILURE);
265 }
266
267 r = fido_dev_make_cred(dev, cred, NULL);
268 if (r == FIDO_ERR_PIN_REQUIRED) {
269 n = snprintf(prompt, sizeof(prompt),
270 "Enter PIN for %s: ", fido_dev_info_path(di));
271 if (n < 0 || (size_t) n >= sizeof(prompt)) {
272 fprintf(stderr, "error: snprintf prompt");
273 exit(EXIT_FAILURE);
274 }
275 if (!readpassphrase(prompt, pin, sizeof(pin), RPP_ECHO_OFF)) {
276 fprintf(stderr, "error: failed to read pin");
277 exit(EXIT_FAILURE);
278 }
279 r = fido_dev_make_cred(dev, cred, pin);
280 }
281 explicit_bzero(pin, sizeof(pin));
282
283 if (r != FIDO_OK) {
284 fprintf(stderr, "error: fido_dev_make_cred (%d) %s\n", r, fido_strerr(r));
285 exit(EXIT_FAILURE);
286 }
287
288 r = fido_cred_verify(cred);
289 if (r != FIDO_OK) {
290 fprintf(stderr, "error: fido_cred_verify (%d) %s\n", r, fido_strerr(r));
291 exit(EXIT_FAILURE);
292 }
293
294 kh = fido_cred_id_ptr(cred);
295 if (!kh) {
296 fprintf(stderr, "error: fido_cred_id_ptr returned NULL\n");
297 exit(EXIT_FAILURE);
298 }
299
300 kh_len = fido_cred_id_len(cred);
301 if (kh_len == 0) {
302 fprintf(stderr, "error: fido_cred_id_len returned 0\n");
303 exit(EXIT_FAILURE);
304 }
305
306 pk = (const unsigned char *) fido_cred_pubkey_ptr(cred);
307 if (!pk) {
308 fprintf(stderr, "error: fido_cred_pubkey_ptr returned NULL\n");
309 exit(EXIT_FAILURE);
310 }
311
312 pk_len = fido_cred_pubkey_len(cred);
313 if (pk_len == 0) {
314 fprintf(stderr, "error: fido_cred_pubkey_len returned 0\n");
315 exit(EXIT_FAILURE);
316 }
317
318 if (!b64_encode(kh, kh_len, &b64_kh)) {
319 fprintf(stderr, "error: failed to encode key handle\n");
320 exit(EXIT_FAILURE);
321 }
322
323 if (!b64_encode(pk, pk_len, &b64_pk)) {
324 fprintf(stderr, "error: failed to encode public key\n");
325 exit(EXIT_FAILURE);
326 }
327
328 if (!args_info.nouser_given)
329 printf("%s", user);
330
331 printf(":%s,%s,%s,%s%s%s", resident_key ? "*" : b64_kh, b64_pk,
332 cose_type == COSE_ES256 ? "es256" : "rs256",
333 user_presence ? "+presence" : "",
334 user_verification ? "+verification" : "",
335 pin_verification ? "+pin" : "");
336
337 exit_code = EXIT_SUCCESS;
338
339 fido_dev_info_free(&devlist, ndevs);
340 fido_cred_free(&cred);
341 fido_dev_free(&dev);
342
343 free(b64_kh);
344 free(b64_pk);
345
346 exit(exit_code);
347 }
348