]>
Commit | Line | Data |
---|---|---|
db9ecf05 | 1 | /* SPDX-License-Identifier: LGPL-2.1-or-later */ |
4368277c | 2 | |
e53d4f34 | 3 | #include "alloc-util.h" |
309a747f | 4 | #include "json-util.h" |
93a1f792 | 5 | #include "log.h" |
8fc798a9 | 6 | #include "openssl-util.h" |
70a5db58 | 7 | #include "user-record-sign.h" |
572c1fe6 | 8 | #include "user-record.h" |
70a5db58 LP |
9 | |
10 | static int user_record_signable_json(UserRecord *ur, char **ret) { | |
11 | _cleanup_(user_record_unrefp) UserRecord *reduced = NULL; | |
309a747f | 12 | _cleanup_(sd_json_variant_unrefp) sd_json_variant *j = NULL; |
70a5db58 LP |
13 | int r; |
14 | ||
15 | assert(ur); | |
16 | assert(ret); | |
17 | ||
bfc0cc1a | 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); |
70a5db58 LP |
19 | if (r < 0) |
20 | return r; | |
21 | ||
309a747f | 22 | j = sd_json_variant_ref(reduced->json); |
70a5db58 | 23 | |
309a747f | 24 | r = sd_json_variant_normalize(&j); |
70a5db58 LP |
25 | if (r < 0) |
26 | return r; | |
27 | ||
309a747f | 28 | return sd_json_variant_format(j, 0, ret); |
70a5db58 LP |
29 | } |
30 | ||
70a5db58 | 31 | int user_record_sign(UserRecord *ur, EVP_PKEY *private_key, UserRecord **ret) { |
309a747f | 32 | _cleanup_(sd_json_variant_unrefp) sd_json_variant *v = NULL; |
70a5db58 | 33 | _cleanup_(user_record_unrefp) UserRecord *signed_ur = NULL; |
70a5db58 | 34 | _cleanup_free_ char *text = NULL, *key = NULL; |
70a5db58 | 35 | _cleanup_free_ void *signature = NULL; |
2485b7e2 | 36 | size_t signature_size = 0; |
70a5db58 LP |
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 | ||
ef65c0f6 LP |
47 | r = digest_and_sign(/* md= */ NULL, private_key, text, SIZE_MAX, &signature, &signature_size); |
48 | if (r < 0) | |
49 | return r; | |
70a5db58 | 50 | |
6a2097dd | 51 | r = openssl_pubkey_to_pem(private_key, &key); |
70a5db58 LP |
52 | if (r < 0) |
53 | return r; | |
54 | ||
309a747f | 55 | v = sd_json_variant_ref(ur->json); |
70a5db58 | 56 | |
309a747f | 57 | r = sd_json_variant_set_fieldb( |
f5fc7732 LP |
58 | &v, |
59 | "signature", | |
309a747f LP |
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))))); | |
70a5db58 LP |
63 | if (r < 0) |
64 | return r; | |
65 | ||
66 | if (DEBUG_LOGGING) | |
309a747f | 67 | sd_json_variant_dump(v, SD_JSON_FORMAT_PRETTY|SD_JSON_FORMAT_COLOR_AUTO, NULL, NULL); |
70a5db58 LP |
68 | |
69 | signed_ur = user_record_new(); | |
70 | if (!signed_ur) | |
71 | return log_oom(); | |
72 | ||
bfc0cc1a | 73 | r = user_record_load(signed_ur, v, USER_RECORD_LOAD_FULL|USER_RECORD_PERMISSIVE); |
70a5db58 LP |
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; | |
309a747f | 84 | sd_json_variant *array, *e; |
70a5db58 LP |
85 | int r; |
86 | ||
87 | assert(ur); | |
88 | assert(public_key); | |
89 | ||
309a747f | 90 | array = sd_json_variant_by_key(ur->json, "signature"); |
70a5db58 LP |
91 | if (!array) |
92 | return USER_RECORD_UNSIGNED; | |
93 | ||
309a747f | 94 | if (!sd_json_variant_is_array(array)) |
70a5db58 LP |
95 | return -EINVAL; |
96 | ||
309a747f | 97 | if (sd_json_variant_elements(array) == 0) |
70a5db58 LP |
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; | |
309a747f | 108 | sd_json_variant *data; |
70a5db58 | 109 | |
309a747f | 110 | if (!sd_json_variant_is_object(e)) |
70a5db58 LP |
111 | return -EINVAL; |
112 | ||
309a747f | 113 | data = sd_json_variant_by_key(e, "data"); |
70a5db58 LP |
114 | if (!data) |
115 | return -EINVAL; | |
116 | ||
309a747f | 117 | r = sd_json_variant_unbase64(data, &signature, &signature_size); |
70a5db58 LP |
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) { | |
b3a9d980 | 129 | n_bad++; |
70a5db58 LP |
130 | continue; |
131 | } | |
132 | ||
b3a9d980 | 133 | n_good++; |
70a5db58 LP |
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) { | |
309a747f | 141 | sd_json_variant *array; |
70a5db58 | 142 | |
309a747f | 143 | array = sd_json_variant_by_key(ur->json, "signature"); |
70a5db58 LP |
144 | if (!array) |
145 | return false; | |
146 | ||
309a747f | 147 | if (!sd_json_variant_is_array(array)) |
70a5db58 LP |
148 | return -EINVAL; |
149 | ||
309a747f | 150 | return sd_json_variant_elements(array) > 0; |
70a5db58 | 151 | } |