1 /*
2  * Copyright 2012 Red Hat, Inc.
3  * All rights reserved.
4  *
5  * This program is free software; you can redistribute it and/or modify
6  * it under the terms of the GNU General Public License as published by
7  * the Free Software Foundation; version 2 of the License.
8  *
9  * This program is distributed in the hope that it will be useful,
10  * but WITHOUT ANY WARRANTY; without even the implied warranty of
11  * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
12  * GNU General Public License for more details.
13  *
14  * You should have received a copy of the GNU General Public License
15  * along with this program.  If not, see <http://www.gnu.org/licenses/>.
16  *
17  * Author(s): Peter Jones <pjones@redhat.com>
18  */
19 
20 #include <unistd.h>
21 #include <sys/mman.h>
22 
23 #include <prerror.h>
24 #include <nss.h>
25 #include <pk11pub.h>
26 #include <secport.h>
27 #include <secerr.h>
28 
29 #include "authvar.h"
30 
31 static char *default_namespace="global";
32 
33 int
authvar_context_init(authvar_context * ctx)34 authvar_context_init(authvar_context *ctx)
35 {
36 	memset(ctx, '\0', sizeof (*ctx));
37 
38 	ctx->namespace = default_namespace;
39 
40 	int rc = cms_context_alloc(&ctx->cms_ctx);
41 	ctx->attr = EFI_VARIABLE_NON_VOLATILE |
42 		    EFI_VARIABLE_RUNTIME_ACCESS |
43 		    EFI_VARIABLE_BOOTSERVICE_ACCESS |
44 		    EFI_VARIABLE_TIME_BASED_AUTHENTICATED_WRITE_ACCESS;
45 	ctx->exportfd = -1;
46 
47 	return rc;
48 }
49 
50 void
authvar_context_fini(authvar_context * ctx)51 authvar_context_fini(authvar_context *ctx)
52 {
53 	if (!ctx)
54 		return;
55 
56 	cms_context_fini(ctx->cms_ctx);
57 
58 	if (ctx->name) {
59 		xfree(ctx->name);
60 	}
61 
62 	if (ctx->valuefile) {
63 		munmap(ctx->value, ctx->value_size);
64 		ctx->value = NULL;
65 
66 		close(ctx->valuefd);
67 		ctx->valuefd = -1;
68 		ctx->value_size = 0;
69 
70 		xfree(ctx->valuefile);
71 		ctx->valuefile = NULL;
72 	} else if (ctx->value) {
73 		xfree(ctx->value);
74 		ctx->value = NULL;
75 		ctx->value_size = 0;
76 	}
77 
78 	if (ctx->exportfd >= 0) {
79 		close(ctx->exportfd);
80 		ctx->exportfd = -1;
81 	}
82 }
83 
84 int
generate_descriptor(authvar_context * ctx)85 generate_descriptor(authvar_context *ctx)
86 {
87 	win_cert_uefi_guid_t *authinfo;
88 	SECItem sd_der;
89 	char *name_ptr;
90 	uint8_t *buf, *ptr;
91 	size_t buf_len;
92 	uint64_t offset;
93 	efi_char16_t *wptr;
94 	int rc;
95 
96 	/* prepare buffer for varname, vendor_guid, attr, timestamp, value */
97 	buf_len = strlen(ctx->name)*sizeof(efi_char16_t) + sizeof(efi_guid_t) +
98 		  sizeof(uint32_t) + sizeof(efi_time_t) + ctx->value_size;
99 	buf = calloc(1, buf_len);
100 	if (!buf)
101 		return -1;
102 
103 	ptr = buf;
104 	name_ptr = ctx->name;
105 	while (*name_ptr != '\0') {
106 		wptr = (efi_char16_t *)ptr;
107 		*wptr = *name_ptr;
108 		name_ptr++;
109 		ptr += sizeof(efi_char16_t);
110 	}
111 
112 	memcpy(ptr, &ctx->guid, sizeof(efi_guid_t));
113 	ptr += sizeof(efi_guid_t);
114 
115 	memcpy(ptr, &ctx->attr, sizeof(uint32_t));
116 	ptr += sizeof(uint32_t);
117 
118 	memcpy(ptr, &ctx->timestamp, sizeof(efi_time_t));
119 	ptr += sizeof(efi_time_t);
120 
121 	memcpy(ptr, ctx->value, ctx->value_size);
122 
123 	ctx->cms_ctx->authbuf_len = buf_len;
124 	ctx->cms_ctx->authbuf = buf;
125 
126 	/* XXX set the value to get SEC_OID_PKCS1_SHA1_WITH_RSA_ENCRYPTION
127 	   from digest_get_signature_oid(). */
128 	ctx->cms_ctx->selected_digest = 0;
129 
130 	/* sign the digest */
131 	memset(&sd_der, '\0', sizeof(sd_der));
132 	rc = generate_authvar_signed_data(ctx->cms_ctx, &sd_der);
133 	if (rc < 0)
134 		cmsreterr(-1, ctx->cms_ctx, "could not create signed data");
135 
136 #if __WORDSIZE == 64
137 	offset = (uint64_t) &((win_cert_uefi_guid_t *)0)->data;
138 #else
139 	offset = (uint32_t) &((win_cert_uefi_guid_t *)0)->data;
140 #endif
141 	authinfo = calloc(offset + sd_der.len, 1);
142 	if (!authinfo)
143 		cmsreterr(-1, ctx->cms_ctx, "could not allocate authinfo");
144 
145 	authinfo->hdr.length = sd_der.len + (uint32_t)offset;
146 	authinfo->hdr.revision = WIN_CERT_REVISION_2_0;
147 	authinfo->hdr.cert_type = WIN_CERT_TYPE_EFI_GUID;
148 	authinfo->type = efi_guid_pkcs7_cert;
149 	memcpy(&authinfo->data, sd_der.data, sd_der.len);
150 
151 	ctx->authinfo = authinfo;
152 
153 	return 0;
154 }
155 
156 int
write_authvar(authvar_context * ctx)157 write_authvar(authvar_context *ctx)
158 {
159 	efi_var_auth_2_t *descriptor;
160 	void *buffer, *ptr;
161 	size_t buf_len, des_len, remain;
162 	ssize_t wlen;
163 
164 	if (!ctx->authinfo)
165 		cmsreterr(-1, ctx->cms_ctx, "Not a valid authvar");
166 
167 	des_len = sizeof(efi_var_auth_2_t) + ctx->authinfo->hdr.length -
168 		  sizeof(win_cert_uefi_guid_t);
169 	buf_len = sizeof(ctx->attr) + des_len + ctx->value_size;
170 
171 	buffer = calloc(buf_len, 1);
172 	if (!buffer)
173 		cmsreterr(-1, ctx->cms_ctx, "could not allocate buffer");
174 	ptr = buffer;
175 
176 	/* The attribute of the variable */
177 	memcpy(ptr, &ctx->attr, sizeof(ctx->attr));
178 	ptr += sizeof(ctx->attr);
179 
180 	/* EFI_VARIABLE_AUTHENTICATION_2 */
181 	descriptor = (efi_var_auth_2_t *)ptr;
182 	memcpy(&descriptor->timestamp, &ctx->timestamp, sizeof(efi_time_t));
183 	memcpy(&descriptor->authinfo, ctx->authinfo, ctx->authinfo->hdr.length);
184 	ptr += des_len;
185 
186 	/* Data */
187 	if (ctx->value_size > 0)
188 		memcpy(ptr, ctx->value, ctx->value_size);
189 
190 	if (!ctx->to_firmware) {
191 		ftruncate(ctx->exportfd, buf_len);
192 		lseek(ctx->exportfd, 0, SEEK_SET);
193 	}
194 
195 	remain = buf_len;
196 	do {
197 		wlen = write(ctx->exportfd, buffer, remain);
198 		if (wlen < 0)
199 			cmsreterr(-1, ctx->cms_ctx, "failed to write authvar");
200 		remain -= wlen;
201 	} while (remain > 0);
202 
203 	return 0;
204 }
205