]> git.ipfire.org Git - thirdparty/systemd.git/blob - src/home/user-record-sign.c
networkctl: use and print full hardware address
[thirdparty/systemd.git] / src / home / user-record-sign.c
1 #include <openssl/pem.h>
2
3 #include "fd-util.h"
4 #include "user-record-sign.h"
5 #include "fileio.h"
6
7 static int user_record_signable_json(UserRecord *ur, char **ret) {
8 _cleanup_(user_record_unrefp) UserRecord *reduced = NULL;
9 _cleanup_(json_variant_unrefp) JsonVariant *j = NULL;
10 int r;
11
12 assert(ur);
13 assert(ret);
14
15 r = user_record_clone(ur, USER_RECORD_REQUIRE_REGULAR|USER_RECORD_ALLOW_PRIVILEGED|USER_RECORD_ALLOW_PER_MACHINE|USER_RECORD_STRIP_SECRET|USER_RECORD_STRIP_BINDING|USER_RECORD_STRIP_STATUS|USER_RECORD_STRIP_SIGNATURE, &reduced);
16 if (r < 0)
17 return r;
18
19 j = json_variant_ref(reduced->json);
20
21 r = json_variant_normalize(&j);
22 if (r < 0)
23 return r;
24
25 return json_variant_format(j, 0, ret);
26 }
27
28 DEFINE_TRIVIAL_CLEANUP_FUNC(EVP_MD_CTX*, EVP_MD_CTX_free);
29
30 int user_record_sign(UserRecord *ur, EVP_PKEY *private_key, UserRecord **ret) {
31 _cleanup_(json_variant_unrefp) JsonVariant *encoded = NULL, *v = NULL;
32 _cleanup_(user_record_unrefp) UserRecord *signed_ur = NULL;
33 _cleanup_(EVP_MD_CTX_freep) EVP_MD_CTX *md_ctx = NULL;
34 _cleanup_free_ char *text = NULL, *key = NULL;
35 size_t signature_size = 0, key_size = 0;
36 _cleanup_free_ void *signature = NULL;
37 _cleanup_fclose_ FILE *mf = NULL;
38 int r;
39
40 assert(ur);
41 assert(private_key);
42 assert(ret);
43
44 r = user_record_signable_json(ur, &text);
45 if (r < 0)
46 return r;
47
48 md_ctx = EVP_MD_CTX_new();
49 if (!md_ctx)
50 return -ENOMEM;
51
52 if (EVP_DigestSignInit(md_ctx, NULL, NULL, NULL, private_key) <= 0)
53 return -EIO;
54
55 /* Request signature size */
56 if (EVP_DigestSign(md_ctx, NULL, &signature_size, (uint8_t*) text, strlen(text)) <= 0)
57 return -EIO;
58
59 signature = malloc(signature_size);
60 if (!signature)
61 return -ENOMEM;
62
63 if (EVP_DigestSign(md_ctx, signature, &signature_size, (uint8_t*) text, strlen(text)) <= 0)
64 return -EIO;
65
66 mf = open_memstream_unlocked(&key, &key_size);
67 if (!mf)
68 return -ENOMEM;
69
70 if (PEM_write_PUBKEY(mf, private_key) <= 0)
71 return -EIO;
72
73 r = fflush_and_check(mf);
74 if (r < 0)
75 return r;
76
77 r = json_build(&encoded, JSON_BUILD_ARRAY(
78 JSON_BUILD_OBJECT(JSON_BUILD_PAIR("data", JSON_BUILD_BASE64(signature, signature_size)),
79 JSON_BUILD_PAIR("key", JSON_BUILD_STRING(key)))));
80 if (r < 0)
81 return r;
82
83 v = json_variant_ref(ur->json);
84
85 r = json_variant_set_field(&v, "signature", encoded);
86 if (r < 0)
87 return r;
88
89 if (DEBUG_LOGGING)
90 json_variant_dump(v, JSON_FORMAT_PRETTY|JSON_FORMAT_COLOR_AUTO, NULL, NULL);
91
92 signed_ur = user_record_new();
93 if (!signed_ur)
94 return log_oom();
95
96 r = user_record_load(signed_ur, v, USER_RECORD_LOAD_FULL);
97 if (r < 0)
98 return r;
99
100 *ret = TAKE_PTR(signed_ur);
101 return 0;
102 }
103
104 int user_record_verify(UserRecord *ur, EVP_PKEY *public_key) {
105 _cleanup_free_ char *text = NULL;
106 unsigned n_good = 0, n_bad = 0;
107 JsonVariant *array, *e;
108 int r;
109
110 assert(ur);
111 assert(public_key);
112
113 array = json_variant_by_key(ur->json, "signature");
114 if (!array)
115 return USER_RECORD_UNSIGNED;
116
117 if (!json_variant_is_array(array))
118 return -EINVAL;
119
120 if (json_variant_elements(array) == 0)
121 return USER_RECORD_UNSIGNED;
122
123 r = user_record_signable_json(ur, &text);
124 if (r < 0)
125 return r;
126
127 JSON_VARIANT_ARRAY_FOREACH(e, array) {
128 _cleanup_(EVP_MD_CTX_freep) EVP_MD_CTX *md_ctx = NULL;
129 _cleanup_free_ void *signature = NULL;
130 size_t signature_size = 0;
131 JsonVariant *data;
132
133 if (!json_variant_is_object(e))
134 return -EINVAL;
135
136 data = json_variant_by_key(e, "data");
137 if (!data)
138 return -EINVAL;
139
140 r = json_variant_unbase64(data, &signature, &signature_size);
141 if (r < 0)
142 return r;
143
144 md_ctx = EVP_MD_CTX_new();
145 if (!md_ctx)
146 return -ENOMEM;
147
148 if (EVP_DigestVerifyInit(md_ctx, NULL, NULL, NULL, public_key) <= 0)
149 return -EIO;
150
151 if (EVP_DigestVerify(md_ctx, signature, signature_size, (uint8_t*) text, strlen(text)) <= 0) {
152 n_bad ++;
153 continue;
154 }
155
156 n_good ++;
157 }
158
159 return n_good > 0 ? (n_bad == 0 ? USER_RECORD_SIGNED_EXCLUSIVE : USER_RECORD_SIGNED) :
160 (n_bad == 0 ? USER_RECORD_UNSIGNED : USER_RECORD_FOREIGN);
161 }
162
163 int user_record_has_signature(UserRecord *ur) {
164 JsonVariant *array;
165
166 array = json_variant_by_key(ur->json, "signature");
167 if (!array)
168 return false;
169
170 if (!json_variant_is_array(array))
171 return -EINVAL;
172
173 return json_variant_elements(array) > 0;
174 }