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