1/*
2 * Copyright (c) 2019 Yubico AB. All rights reserved.
3 * Use of this source code is governed by a BSD-style
4 * license that can be found in the LICENSE file.
5 * SPDX-License-Identifier: BSD-2-Clause
6 */
7
8#include <fido.h>
9#include <fido/credman.h>
10
11#include <stdio.h>
12#include <stdlib.h>
13#include <string.h>
14#ifdef HAVE_UNISTD_H
15#include <unistd.h>
16#endif
17
18#include "../openbsd-compat/openbsd-compat.h"
19#include "extern.h"
20
21int
22credman_get_metadata(fido_dev_t *dev, const char *path)
23{
24	fido_credman_metadata_t *metadata = NULL;
25	char *pin = NULL;
26	int r, ok = 1;
27
28	if ((metadata = fido_credman_metadata_new()) == NULL) {
29		warnx("fido_credman_metadata_new");
30		goto out;
31	}
32	if ((r = fido_credman_get_dev_metadata(dev, metadata,
33	    NULL)) != FIDO_OK && should_retry_with_pin(dev, r)) {
34		if ((pin = get_pin(path)) == NULL)
35			goto out;
36		r = fido_credman_get_dev_metadata(dev, metadata, pin);
37		freezero(pin, PINBUF_LEN);
38		pin = NULL;
39	}
40	if (r != FIDO_OK) {
41		warnx("fido_credman_get_dev_metadata: %s", fido_strerr(r));
42		goto out;
43	}
44
45	printf("existing rk(s): %u\n",
46	    (unsigned)fido_credman_rk_existing(metadata));
47	printf("remaining rk(s): %u\n",
48	    (unsigned)fido_credman_rk_remaining(metadata));
49
50	ok = 0;
51out:
52	fido_credman_metadata_free(&metadata);
53	fido_dev_close(dev);
54	fido_dev_free(&dev);
55
56	exit(ok);
57}
58
59static int
60print_rp(fido_credman_rp_t *rp, size_t idx)
61{
62	char *rp_id_hash = NULL;
63
64	if (base64_encode(fido_credman_rp_id_hash_ptr(rp, idx),
65	    fido_credman_rp_id_hash_len(rp, idx), &rp_id_hash) < 0) {
66		warnx("output error");
67		return -1;
68	}
69	printf("%02u: %s %s\n", (unsigned)idx, rp_id_hash,
70	    fido_credman_rp_id(rp, idx));
71	free(rp_id_hash);
72
73	return 0;
74}
75
76int
77credman_list_rp(const char *path)
78{
79	fido_credman_rp_t *rp = NULL;
80	fido_dev_t *dev = NULL;
81	char *pin = NULL;
82	int r, ok = 1;
83
84	dev = open_dev(path);
85	if ((rp = fido_credman_rp_new()) == NULL) {
86		warnx("fido_credman_rp_new");
87		goto out;
88	}
89	if ((r = fido_credman_get_dev_rp(dev, rp, NULL)) != FIDO_OK &&
90	    should_retry_with_pin(dev, r)) {
91		if ((pin = get_pin(path)) == NULL)
92			goto out;
93		r = fido_credman_get_dev_rp(dev, rp, pin);
94		freezero(pin, PINBUF_LEN);
95		pin = NULL;
96	}
97	if (r != FIDO_OK) {
98		warnx("fido_credman_get_dev_rp: %s", fido_strerr(r));
99		goto out;
100	}
101	for (size_t i = 0; i < fido_credman_rp_count(rp); i++)
102		if (print_rp(rp, i) < 0)
103			goto out;
104
105	ok = 0;
106out:
107	fido_credman_rp_free(&rp);
108	fido_dev_close(dev);
109	fido_dev_free(&dev);
110
111	exit(ok);
112}
113
114static int
115print_rk(const fido_credman_rk_t *rk, size_t idx)
116{
117	const fido_cred_t *cred;
118	char *id = NULL;
119	char *user_id = NULL;
120	const char *type;
121	const char *prot;
122
123	if ((cred = fido_credman_rk(rk, idx)) == NULL) {
124		warnx("fido_credman_rk");
125		return -1;
126	}
127	if (base64_encode(fido_cred_id_ptr(cred), fido_cred_id_len(cred),
128	    &id) < 0 || base64_encode(fido_cred_user_id_ptr(cred),
129	    fido_cred_user_id_len(cred), &user_id) < 0) {
130		warnx("output error");
131		return -1;
132	}
133
134	type = cose_string(fido_cred_type(cred));
135	prot = prot_string(fido_cred_prot(cred));
136
137	printf("%02u: %s %s %s %s %s\n", (unsigned)idx, id,
138	    fido_cred_display_name(cred), user_id, type, prot);
139
140	free(user_id);
141	free(id);
142
143	return 0;
144}
145
146int
147credman_list_rk(const char *path, const char *rp_id)
148{
149	fido_dev_t *dev = NULL;
150	fido_credman_rk_t *rk = NULL;
151	char *pin = NULL;
152	int r, ok = 1;
153
154	dev = open_dev(path);
155	if ((rk = fido_credman_rk_new()) == NULL) {
156		warnx("fido_credman_rk_new");
157		goto out;
158	}
159	if ((r = fido_credman_get_dev_rk(dev, rp_id, rk, NULL)) != FIDO_OK &&
160	    should_retry_with_pin(dev, r)) {
161		if ((pin = get_pin(path)) == NULL)
162			goto out;
163		r = fido_credman_get_dev_rk(dev, rp_id, rk, pin);
164		freezero(pin, PINBUF_LEN);
165		pin = NULL;
166	}
167	if (r != FIDO_OK) {
168		warnx("fido_credman_get_dev_rk: %s", fido_strerr(r));
169		goto out;
170	}
171	for (size_t i = 0; i < fido_credman_rk_count(rk); i++)
172		if (print_rk(rk, i) < 0)
173			goto out;
174
175	ok = 0;
176out:
177	fido_credman_rk_free(&rk);
178	fido_dev_close(dev);
179	fido_dev_free(&dev);
180
181	exit(ok);
182}
183
184int
185credman_print_rk(fido_dev_t *dev, const char *path, const char *rp_id,
186    const char *cred_id)
187{
188	fido_credman_rk_t *rk = NULL;
189	const fido_cred_t *cred = NULL;
190	char *pin = NULL;
191	void *cred_id_ptr = NULL;
192	size_t cred_id_len = 0;
193	int r, ok = 1;
194
195	if ((rk = fido_credman_rk_new()) == NULL) {
196		warnx("fido_credman_rk_new");
197		goto out;
198	}
199	if (base64_decode(cred_id, &cred_id_ptr, &cred_id_len) < 0) {
200		warnx("base64_decode");
201		goto out;
202	}
203	if ((r = fido_credman_get_dev_rk(dev, rp_id, rk, NULL)) != FIDO_OK &&
204	    should_retry_with_pin(dev, r)) {
205		if ((pin = get_pin(path)) == NULL)
206			goto out;
207		r = fido_credman_get_dev_rk(dev, rp_id, rk, pin);
208		freezero(pin, PINBUF_LEN);
209		pin = NULL;
210	}
211	if (r != FIDO_OK) {
212		warnx("fido_credman_get_dev_rk: %s", fido_strerr(r));
213		goto out;
214	}
215
216	for (size_t i = 0; i < fido_credman_rk_count(rk); i++) {
217		if ((cred = fido_credman_rk(rk, i)) == NULL ||
218		    fido_cred_id_ptr(cred) == NULL) {
219			warnx("output error");
220			goto out;
221		}
222		if (cred_id_len != fido_cred_id_len(cred) ||
223		    memcmp(cred_id_ptr, fido_cred_id_ptr(cred), cred_id_len))
224			continue;
225		print_cred(stdout, fido_cred_type(cred), cred);
226		ok = 0;
227		goto out;
228	}
229
230	warnx("credential not found");
231out:
232	free(cred_id_ptr);
233	fido_credman_rk_free(&rk);
234	fido_dev_close(dev);
235	fido_dev_free(&dev);
236
237	exit(ok);
238}
239
240int
241credman_delete_rk(const char *path, const char *id)
242{
243	fido_dev_t *dev = NULL;
244	char *pin = NULL;
245	void *id_ptr = NULL;
246	size_t id_len = 0;
247	int r, ok = 1;
248
249	dev = open_dev(path);
250	if (base64_decode(id, &id_ptr, &id_len) < 0) {
251		warnx("base64_decode");
252		goto out;
253	}
254	if ((r = fido_credman_del_dev_rk(dev, id_ptr, id_len,
255	    NULL)) != FIDO_OK && should_retry_with_pin(dev, r)) {
256		if ((pin = get_pin(path)) == NULL)
257			goto out;
258		r = fido_credman_del_dev_rk(dev, id_ptr, id_len, pin);
259		freezero(pin, PINBUF_LEN);
260		pin = NULL;
261	}
262	if (r != FIDO_OK) {
263		warnx("fido_credman_del_dev_rk: %s", fido_strerr(r));
264		goto out;
265	}
266
267	ok = 0;
268out:
269	free(id_ptr);
270	fido_dev_close(dev);
271	fido_dev_free(&dev);
272
273	exit(ok);
274}
275
276int
277credman_update_rk(const char *path, const char *user_id, const char *cred_id,
278    const char *name, const char *display_name)
279{
280	fido_dev_t *dev = NULL;
281	fido_cred_t *cred = NULL;
282	char *pin = NULL;
283	void *user_id_ptr = NULL;
284	void *cred_id_ptr = NULL;
285	size_t user_id_len = 0;
286	size_t cred_id_len = 0;
287	int r, ok = 1;
288
289	dev = open_dev(path);
290	if (base64_decode(user_id, &user_id_ptr, &user_id_len) < 0 ||
291	    base64_decode(cred_id, &cred_id_ptr, &cred_id_len) < 0) {
292		warnx("base64_decode");
293		goto out;
294	}
295	if ((cred = fido_cred_new()) == NULL) {
296		warnx("fido_cred_new");
297		goto out;
298	}
299	if ((r = fido_cred_set_id(cred, cred_id_ptr, cred_id_len)) != FIDO_OK) {
300		warnx("fido_cred_set_id: %s",  fido_strerr(r));
301		goto out;
302	}
303	if ((r = fido_cred_set_user(cred, user_id_ptr, user_id_len, name,
304	    display_name, NULL)) != FIDO_OK) {
305		warnx("fido_cred_set_user: %s", fido_strerr(r));
306		goto out;
307	}
308	if ((r = fido_credman_set_dev_rk(dev, cred, NULL)) != FIDO_OK &&
309	    should_retry_with_pin(dev, r)) {
310		if ((pin = get_pin(path)) == NULL)
311			goto out;
312		r = fido_credman_set_dev_rk(dev, cred, pin);
313		freezero(pin, PINBUF_LEN);
314		pin = NULL;
315	}
316	if (r != FIDO_OK) {
317		warnx("fido_credman_set_dev_rk: %s", fido_strerr(r));
318		goto out;
319	}
320
321	ok = 0;
322out:
323	free(user_id_ptr);
324	free(cred_id_ptr);
325	fido_dev_close(dev);
326	fido_dev_free(&dev);
327	fido_cred_free(&cred);
328
329	exit(ok);
330}
331