]> git.ipfire.org Git - thirdparty/systemd.git/blob - src/home/user-record-sign.c
367ab38330a0faff3af4f3e3bf4aece631f755ea
[thirdparty/systemd.git] / src / home / user-record-sign.c
1 /* SPDX-License-Identifier: LGPL-2.1-or-later */
2
3 #include "alloc-util.h"
4 #include "json-util.h"
5 #include "log.h"
6 #include "openssl-util.h"
7 #include "user-record-sign.h"
8 #include "user-record.h"
9
10 static int user_record_signable_json(UserRecord *ur, char **ret) {
11 _cleanup_(user_record_unrefp) UserRecord *reduced = NULL;
12 _cleanup_(sd_json_variant_unrefp) sd_json_variant *j = NULL;
13 int r;
14
15 assert(ur);
16 assert(ret);
17
18 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|USER_RECORD_PERMISSIVE, &reduced);
19 if (r < 0)
20 return r;
21
22 j = sd_json_variant_ref(reduced->json);
23
24 r = sd_json_variant_normalize(&j);
25 if (r < 0)
26 return r;
27
28 return sd_json_variant_format(j, 0, ret);
29 }
30
31 int user_record_sign(UserRecord *ur, EVP_PKEY *private_key, UserRecord **ret) {
32 _cleanup_(sd_json_variant_unrefp) sd_json_variant *v = NULL;
33 _cleanup_(user_record_unrefp) UserRecord *signed_ur = NULL;
34 _cleanup_free_ char *text = NULL, *key = NULL;
35 _cleanup_free_ void *signature = NULL;
36 size_t signature_size = 0;
37 int r;
38
39 assert(ur);
40 assert(private_key);
41 assert(ret);
42
43 r = user_record_signable_json(ur, &text);
44 if (r < 0)
45 return r;
46
47 r = digest_and_sign(/* md= */ NULL, private_key, text, SIZE_MAX, &signature, &signature_size);
48 if (r < 0)
49 return r;
50
51 r = openssl_pubkey_to_pem(private_key, &key);
52 if (r < 0)
53 return r;
54
55 v = sd_json_variant_ref(ur->json);
56
57 r = sd_json_variant_set_fieldb(
58 &v,
59 "signature",
60 SD_JSON_BUILD_ARRAY(
61 SD_JSON_BUILD_OBJECT(SD_JSON_BUILD_PAIR("data", SD_JSON_BUILD_BASE64(signature, signature_size)),
62 SD_JSON_BUILD_PAIR("key", SD_JSON_BUILD_STRING(key)))));
63 if (r < 0)
64 return r;
65
66 if (DEBUG_LOGGING)
67 sd_json_variant_dump(v, SD_JSON_FORMAT_PRETTY|SD_JSON_FORMAT_COLOR_AUTO, NULL, NULL);
68
69 signed_ur = user_record_new();
70 if (!signed_ur)
71 return log_oom();
72
73 r = user_record_load(signed_ur, v, USER_RECORD_LOAD_FULL|USER_RECORD_PERMISSIVE);
74 if (r < 0)
75 return r;
76
77 *ret = TAKE_PTR(signed_ur);
78 return 0;
79 }
80
81 int user_record_verify(UserRecord *ur, EVP_PKEY *public_key) {
82 _cleanup_free_ char *text = NULL;
83 unsigned n_good = 0, n_bad = 0;
84 sd_json_variant *array, *e;
85 int r;
86
87 assert(ur);
88 assert(public_key);
89
90 array = sd_json_variant_by_key(ur->json, "signature");
91 if (!array)
92 return USER_RECORD_UNSIGNED;
93
94 if (!sd_json_variant_is_array(array))
95 return -EINVAL;
96
97 if (sd_json_variant_elements(array) == 0)
98 return USER_RECORD_UNSIGNED;
99
100 r = user_record_signable_json(ur, &text);
101 if (r < 0)
102 return r;
103
104 JSON_VARIANT_ARRAY_FOREACH(e, array) {
105 _cleanup_(EVP_MD_CTX_freep) EVP_MD_CTX *md_ctx = NULL;
106 _cleanup_free_ void *signature = NULL;
107 size_t signature_size = 0;
108 sd_json_variant *data;
109
110 if (!sd_json_variant_is_object(e))
111 return -EINVAL;
112
113 data = sd_json_variant_by_key(e, "data");
114 if (!data)
115 return -EINVAL;
116
117 r = sd_json_variant_unbase64(data, &signature, &signature_size);
118 if (r < 0)
119 return r;
120
121 md_ctx = EVP_MD_CTX_new();
122 if (!md_ctx)
123 return -ENOMEM;
124
125 if (EVP_DigestVerifyInit(md_ctx, NULL, NULL, NULL, public_key) <= 0)
126 return -EIO;
127
128 if (EVP_DigestVerify(md_ctx, signature, signature_size, (uint8_t*) text, strlen(text)) <= 0) {
129 n_bad++;
130 continue;
131 }
132
133 n_good++;
134 }
135
136 return n_good > 0 ? (n_bad == 0 ? USER_RECORD_SIGNED_EXCLUSIVE : USER_RECORD_SIGNED) :
137 (n_bad == 0 ? USER_RECORD_UNSIGNED : USER_RECORD_FOREIGN);
138 }
139
140 int user_record_has_signature(UserRecord *ur) {
141 sd_json_variant *array;
142
143 array = sd_json_variant_by_key(ur->json, "signature");
144 if (!array)
145 return false;
146
147 if (!sd_json_variant_is_array(array))
148 return -EINVAL;
149
150 return sd_json_variant_elements(array) > 0;
151 }