Skip to content

Commit 803bf5f

Browse files
committed
add sbattach tool
Signed-off-by: Stephan Mueller <smueller@chronox.de>
1 parent f8ea216 commit 803bf5f

File tree

6 files changed

+816
-1
lines changed

6 files changed

+816
-1
lines changed

CHANGES.md

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -37,7 +37,7 @@ Changes 1.7.0-prerelease
3737

3838
* Add support of PEM
3939

40-
* Add sbsign, sbverify, sbsiglist, sbvarsign, sbkeysync (currently untested as its usage is not fully clear) tools as a drop-in replacement for the one provided with http://git.kernel.org/pub/scm/linux/kernel/git/jejb/sbsigntools.git
40+
* Add sbsign, sbverify, sbsiglist, sbvarsign, sbattach, sbkeysync (currently untested as its usage is not fully clear) tools as a drop-in replacement for the one provided with http://git.kernel.org/pub/scm/linux/kernel/git/jejb/sbsigntools.git
4141

4242
* PKCS7 generator: support altering the generation process (allow content type setting, additional authenticated attributes)
4343

LICENSE

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -611,3 +611,4 @@ apps/src/coff/external.h
611611
apps/src/efivars.h
612612
apps/src/sbvarsign.c
613613
apps/src/sbsiglist.c
614+
apps/src/sbattach.c

apps/src/sbattach.c

Lines changed: 337 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,337 @@
1+
/*
2+
* Copyright (C) 2026, Stephan Mueller <smueller@chronox.de>
3+
*
4+
* License: see LICENSE file in root directory
5+
*
6+
* THIS SOFTWARE IS PROVIDED ``AS IS'' AND ANY EXPRESS OR IMPLIED
7+
* WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES
8+
* OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE, ALL OF
9+
* WHICH ARE HEREBY DISCLAIMED. IN NO EVENT SHALL THE AUTHOR BE
10+
* LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR
11+
* CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT
12+
* OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR
13+
* BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF
14+
* LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
15+
* (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE
16+
* USE OF THIS SOFTWARE, EVEN IF NOT ADVISED OF THE POSSIBILITY OF SUCH
17+
* DAMAGE.
18+
*/
19+
/*
20+
* This implementation is intended to provide a drop-in replacement for the
21+
* sbsign tool from
22+
* http://git.kernel.org/pub/scm/linux/kernel/git/jejb/sbsigntools.git.
23+
*
24+
* The file is derived from this code with the following license:
25+
*/
26+
/*
27+
* Copyright (C) 2012 Jeremy Kerr <jeremy.kerr@canonical.com>
28+
*
29+
* This program is free software; you can redistribute it and/or
30+
* modify it under the terms of the GNU General Public License
31+
* as published by the Free Software Foundation; either version 3
32+
* of the License, or (at your option) any later version.
33+
*
34+
* This program is distributed in the hope that it will be useful,
35+
* but WITHOUT ANY WARRANTY; without even the implied warranty of
36+
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
37+
* GNU General Public License for more details.
38+
*
39+
* You should have received a copy of the GNU General Public License
40+
* along with this program; if not, write to the Free Software
41+
* Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301,
42+
* USA.
43+
*
44+
* In addition, as a special exception, the copyright holders give
45+
* permission to link the code of portions of this program with the OpenSSL
46+
* library under certain conditions as described in each individual source file,
47+
* and distribute linked combinations including the two.
48+
*
49+
* You must obey the GNU General Public License in all respects for all
50+
* of the code used other than OpenSSL. If you modify file(s) with this
51+
* exception, you may extend this exception to your version of the
52+
* file(s), but you are not obligated to do so. If you do not wish to do
53+
* so, delete this exception statement from your version. If you delete
54+
* this exception statement from all source files in the program, then
55+
* also delete it here.
56+
*/
57+
#define _GNU_SOURCE
58+
59+
#include <errno.h>
60+
#include <stdbool.h>
61+
#include <stdio.h>
62+
#include <stdlib.h>
63+
#include <stdint.h>
64+
#include <unistd.h>
65+
#include <sys/stat.h>
66+
#include <sys/types.h>
67+
#include <fcntl.h>
68+
#include <string.h>
69+
70+
#include <getopt.h>
71+
72+
#include "image.h"
73+
74+
#include "asn1.h"
75+
#include "lc_pkcs7_generator_helper.h"
76+
#include "lc_x509_generator_file_helper.h"
77+
#include "ret_checkers.h"
78+
#include "lc_status.h"
79+
#include "small_stack_support.h"
80+
#include "x509_print.h"
81+
82+
static const char *toolname = "sbattach";
83+
84+
static struct option options[] = {
85+
{ "attach", required_argument, NULL, 'a' },
86+
{ "detach", required_argument, NULL, 'd' },
87+
{ "remove", no_argument, NULL, 'r' },
88+
{ "help", no_argument, NULL, 'h' },
89+
{ "version", no_argument, NULL, 'V' },
90+
{ "signum", required_argument, NULL, 's' },
91+
{ "print", no_argument, NULL, 'p' },
92+
{ NULL, 0, NULL, 0 },
93+
};
94+
95+
static void usage(void)
96+
{
97+
printf("Usage: %s --attach <sigfile> <efi-boot-image>\n"
98+
" or: %s --detach <sigfile> [--remove] <efi-boot-image>\n"
99+
" or: %s --remove <efi-boot-image>\n"
100+
"Attach or detach a signature file to/from a boot image\n"
101+
"\n"
102+
"Options:\n"
103+
"\t--attach <sigfile> set <sigfile> as the boot image's\n"
104+
"\t signature table\n"
105+
"\t--detach <sigfile> copy the boot image's signature table\n"
106+
"\t to <sigfile>\n"
107+
"\t--remove remove the boot image's signature\n"
108+
"\t table from the original file\n"
109+
"\t--signum signature to operate on (defaults to\n"
110+
"\t first)\n"
111+
"\t--print Verify PKCS#7 message and print content\n",
112+
toolname, toolname, toolname);
113+
}
114+
115+
static void version(void)
116+
{
117+
char version[500];
118+
119+
memset(version, 0, sizeof(version));
120+
lc_status(version, sizeof(version));
121+
122+
fprintf(stderr, "Leancrypto %s\n", toolname);
123+
fprintf(stderr, "%s\n", version);
124+
}
125+
126+
static int detach_sig(struct image *image, unsigned int signum,
127+
const char *sig_filename)
128+
{
129+
return image_write_detached(image, signum, sig_filename);
130+
}
131+
132+
static int pkcs7_ver_message_sbattach(struct pkcs7_generator_opts *opts,
133+
const uint8_t *pkcs7_data,
134+
size_t pkcs7_datalen)
135+
{
136+
struct lc_pkcs7_parse_context ctx;
137+
uint8_t *buf = NULL;
138+
PKCS7_ALLOC
139+
int ret;
140+
141+
CKINT(lc_pkcs7_decode_ctx_init(&ctx));
142+
143+
CKINT(lc_pkcs7_decode_ctx_set_aa_content_type(&ctx,
144+
OID_msIndirectData));
145+
146+
/* Set the PKCS#7 message structure to be encoded */
147+
CKINT(lc_pkcs7_decode_ctx_set_pkcs7(&ctx, pkcs7_msg));
148+
149+
CKINT_LOG(lc_pkcs7_decode_ctx(&ctx, pkcs7_data, pkcs7_datalen),
150+
"Parsing of input data failed\n");
151+
152+
/* No data to be set as data is embedded */
153+
154+
ret = lc_pkcs7_verify(
155+
pkcs7_msg, opts->use_trust_store ? &opts->trust_store : NULL,
156+
opts->verify_rules_set ? &opts->verify_rules : NULL);
157+
if (!opts->skip_signature_verification) {
158+
if (ret) {
159+
printf("Verification of PKCS#7 message failed\n");
160+
goto out;
161+
}
162+
} else {
163+
if (ret == -EBADMSG) {
164+
printf("AA: Message digest size mismatch\n");
165+
goto out;
166+
} else if (ret == -EKEYREJECTED) {
167+
printf("AA: No message digest or digest mismatch\n");
168+
goto out;
169+
} else if (ret == -ENOKEY) {
170+
printf("No signer found - skipping signature verification as requested\n");
171+
}
172+
}
173+
174+
if (opts->print_pkcs7)
175+
CKINT(print_pkcs7_data(pkcs7_msg));
176+
177+
out:
178+
lc_free(buf);
179+
lc_pkcs7_message_clear(pkcs7_msg);
180+
lc_pkcs7_decode_ctx_clear(&ctx);
181+
PKCS7_FREE
182+
return ret;
183+
}
184+
185+
static int attach_sig(struct pkcs7_generator_opts *opts, struct image *image,
186+
const char *image_filename, const char *sig_filename)
187+
{
188+
struct workspace {
189+
struct image image;
190+
uint8_t data[ASN1_MAX_DATASIZE];
191+
};
192+
uint8_t *sigbuf;
193+
size_t size;
194+
int ret;
195+
LC_DECLARE_MEM(ws, struct workspace, sizeof(uint64_t));
196+
197+
CKINT(get_data(sig_filename, &sigbuf, &size, lc_pem_flag_nopem));
198+
199+
CKINT(image_add_signature(image, sigbuf, size));
200+
201+
CKINT(pkcs7_ver_message_sbattach(opts, sigbuf, size));
202+
203+
CKINT_LOG(image_write(image, image_filename),
204+
"Error writing %s: %s\n", image_filename, strerror(errno));
205+
206+
out:
207+
release_data(sigbuf, size, lc_pem_flag_nopem);
208+
image_release(&ws->image);
209+
LC_RELEASE_MEM(ws);
210+
return ret;
211+
}
212+
213+
static int remove_sig(struct image *image, unsigned int signum,
214+
const char *image_filename)
215+
{
216+
int ret;
217+
218+
CKINT_LOG(image_remove_signature(image, signum),
219+
"Error, image has no signature at %u\n", signum + 1);
220+
221+
CKINT_LOG(image_write(image, image_filename),
222+
"Error writing %s: %s\n", image_filename, strerror(errno));
223+
224+
out:
225+
return ret;
226+
}
227+
228+
enum action {
229+
ACTION_NONE,
230+
ACTION_ATTACH,
231+
ACTION_DETACH,
232+
};
233+
234+
int main(int argc, char **argv)
235+
{
236+
struct workspace {
237+
struct image image;
238+
struct pkcs7_generator_opts parsed_opts;
239+
};
240+
uint8_t *image_buf = NULL;
241+
size_t image_size = 0;
242+
unsigned long val;
243+
enum action action;
244+
int c, ret = 0;
245+
unsigned int signum = 0;
246+
bool remove;
247+
LC_DECLARE_MEM(ws, struct workspace, sizeof(uint64_t));
248+
249+
action = ACTION_NONE;
250+
remove = false;
251+
252+
for (;;) {
253+
int idx;
254+
c = getopt_long(argc, argv, "a:d:s:rhVp", options, &idx);
255+
if (c == -1)
256+
break;
257+
258+
switch (c) {
259+
case 'a':
260+
case 'd':
261+
if (action != ACTION_NONE) {
262+
fprintf(stderr, "Multiple actions specified\n");
263+
usage();
264+
ret = -EINVAL;
265+
goto out;
266+
}
267+
action = (c == 'a') ? ACTION_ATTACH : ACTION_DETACH;
268+
ws->parsed_opts.outfile = optarg;
269+
break;
270+
case 's':
271+
val = strtoul(optarg, NULL, 10);
272+
if (val >= UINT_MAX)
273+
return -EINVAL;
274+
275+
/* humans count from 1 not zero */
276+
signum = (unsigned int)val - 1;
277+
break;
278+
case 'r':
279+
remove = true;
280+
break;
281+
case 'V':
282+
version();
283+
goto out;
284+
case 'h':
285+
usage();
286+
goto out;
287+
case 'p':
288+
ws->parsed_opts.print_pkcs7 = true;
289+
break;
290+
}
291+
}
292+
293+
if (argc != optind + 1) {
294+
usage();
295+
ret = -EINVAL;
296+
goto out;
297+
}
298+
ws->parsed_opts.infile = argv[optind];
299+
300+
/* sanity check action combinations */
301+
if (action == ACTION_ATTACH && remove) {
302+
fprintf(stderr, "Can't use --remove with --attach\n");
303+
ret = -EINVAL;
304+
goto out;
305+
}
306+
307+
if (action == ACTION_NONE && !remove) {
308+
fprintf(stderr, "No action (attach/detach/remove) specified\n");
309+
usage();
310+
ret = -EINVAL;
311+
goto out;
312+
}
313+
314+
CKINT(get_data_memory(ws->parsed_opts.infile, &image_buf, &image_size,
315+
lc_pem_flag_nopem));
316+
CKINT_LOG(image_load(image_buf, image_size, &ws->image),
317+
"Can't load image file %s\n", ws->parsed_opts.infile);
318+
319+
if (action == ACTION_ATTACH) {
320+
CKINT(attach_sig(&ws->parsed_opts, &ws->image,
321+
ws->parsed_opts.infile,
322+
ws->parsed_opts.outfile));
323+
} else if (action == ACTION_DETACH) {
324+
CKINT(detach_sig(&ws->image, signum, ws->parsed_opts.outfile));
325+
}
326+
327+
if (remove) {
328+
CKINT(remove_sig(&ws->image, signum, ws->parsed_opts.infile));
329+
}
330+
331+
out:
332+
release_data_memory(image_buf, image_size, lc_pem_flag_nopem);
333+
pkcs7_clean_opts(&ws->parsed_opts);
334+
image_release(&ws->image);
335+
LC_RELEASE_MEM(ws);
336+
return -ret;
337+
}

0 commit comments

Comments
 (0)