• Home
  • Line#
  • Scopes#
  • Navigate#
  • Raw
  • Download
1 /*############################################################################
2   # Copyright 2016-2017 Intel Corporation
3   #
4   # Licensed under the Apache License, Version 2.0 (the "License");
5   # you may not use this file except in compliance with the License.
6   # You may obtain a copy of the License at
7   #
8   #     http://www.apache.org/licenses/LICENSE-2.0
9   #
10   # Unless required by applicable law or agreed to in writing, software
11   # distributed under the License is distributed on an "AS IS" BASIS,
12   # WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
13   # See the License for the specific language governing permissions and
14   # limitations under the License.
15   ############################################################################*/
16 
17 /*!
18  * \file
19  *
20  * \brief Create group revocation list request
21  *
22  */
23 
24 #include <argtable3.h>
25 #include <stdlib.h>
26 #include <string.h>
27 #include "epid/common/file_parser.h"
28 #include "util/buffutil.h"
29 #include "util/envutil.h"
30 #include "util/stdtypes.h"
31 
32 const OctStr16 kEpidFileVersion = {2, 0};
33 
34 // Defaults
35 #define PROGRAM_NAME "revokegrp"
36 #define PUBKEYFILE_DEFAULT "pubkey.bin"
37 #define REQFILE_DEFAULT "grprlreq.dat"
38 #define REASON_DEFAULT 0
39 #define GROUP_PUB_KEY_SIZE \
40   (sizeof(EpidFileHeader) + sizeof(GroupPubKey) + sizeof(EcdsaSignature))
41 #define ARGPARSE_ERROR_MAX 20
42 #define ARGTABLE_SIZE 7
43 
44 // Defined function to get defined number as string
45 #define STRINGIZE_(x) #x
46 #define STRINGIZE(x) STRINGIZE_(x)
47 
48 #pragma pack(1)
49 /// Group revocation request entry
50 typedef struct GrpInfo {
51   GroupId gid;     ///< Intel(R) EPID Group ID
52   uint8_t reason;  ///< Revocation reason
53 } GrpInfo;
54 /// Group Revocation request
55 typedef struct GrpRlRequest {
56   EpidFileHeader header;  ///< Intel(R) EPID File Header
57   uint32_t count;         ///< Revoked count (big endian)
58   GrpInfo groups[1];      ///< Revoked group count (flexible array)
59 } GrpRlRequest;
60 #pragma pack()
61 
62 /// convert host to network byte order
htonl(uint32_t hostlong)63 static uint32_t htonl(uint32_t hostlong) {
64   return (((hostlong & 0xFF) << 24) | ((hostlong & 0xFF00) << 8) |
65           ((hostlong & 0xFF0000) >> 8) | ((hostlong & 0xFF000000) >> 24));
66 }
67 /// convert network to host byte order
ntohl(uint32_t netlong)68 static uint32_t ntohl(uint32_t netlong) {
69   return (((netlong & 0xFF) << 24) | ((netlong & 0xFF00) << 8) |
70           ((netlong & 0xFF0000) >> 8) | ((netlong & 0xFF000000) >> 24));
71 }
72 
73 /// Makes a request and appends it to file.
74 /*!
75 \param[in] cacert_file
76 Issuing CA certificate used to sign group public key file.
77 \param[in] pubkey_file
78 File containing group public key.
79 \param[in] req_file
80 File to write a request.
81 \param[in] reason
82 Revokation reason.
83 \param[in] verbose
84 If true function would print debug information to stdout.
85 */
86 int MakeRequest(char const* cacert_file, char const* pubkey_file,
87                 char const* req_file, uint8_t reason, bool verbose);
88 
89 /// Main entrypoint
main(int argc,char * argv[])90 int main(int argc, char* argv[]) {
91   // intermediate return value for C style functions
92   int ret_value = EXIT_FAILURE;
93 
94   // User Settings
95 
96   // Verbose flag parameter
97   static bool verbose_flag = false;
98 
99   struct arg_file* pubkey_file = arg_file0(
100       NULL, "gpubkey", "FILE",
101       "load group public key from FILE (default: " PUBKEYFILE_DEFAULT ")");
102   struct arg_file* cacert_file = arg_file1(
103       NULL, "capubkey", "FILE", "load IoT Issuing CA public key from FILE");
104   struct arg_int* reason =
105       arg_int0(NULL, "reason", "NUM",
106                "revocation reason (default: " STRINGIZE(REASON_DEFAULT) ")");
107   struct arg_file* req_file = arg_file0(
108       NULL, "req", "FILE",
109       "append signature revocation request to FILE (default: " REQFILE_DEFAULT
110       ")");
111   struct arg_lit* help = arg_lit0(NULL, "help", "display this help and exit");
112   struct arg_lit* verbose =
113       arg_lit0("v", "verbose", "print status messages to stdout");
114   struct arg_end* end = arg_end(ARGPARSE_ERROR_MAX);
115   void* argtable[ARGTABLE_SIZE];
116   int nerrors;
117 
118   /* initialize the argtable array with ptrs to the arg_xxx structures
119    * constructed above */
120   argtable[0] = pubkey_file;
121   argtable[1] = cacert_file;
122   argtable[2] = reason;
123   argtable[3] = req_file;
124   argtable[4] = help;
125   argtable[5] = verbose;
126   argtable[6] = end;
127 
128   // set program name for logging
129   set_prog_name(PROGRAM_NAME);
130   do {
131     /* verify the argtable[] entries were allocated sucessfully */
132     if (arg_nullcheck(argtable) != 0) {
133       /* NULL entries were detected, some allocations must have failed */
134       printf("%s: insufficient memory\n", PROGRAM_NAME);
135       ret_value = EXIT_FAILURE;
136       break;
137     }
138 
139     /* set any command line default values prior to parsing */
140     pubkey_file->filename[0] = PUBKEYFILE_DEFAULT;
141     req_file->filename[0] = REQFILE_DEFAULT;
142     reason->ival[0] = REASON_DEFAULT;
143 
144     /* Parse the command line as defined by argtable[] */
145     nerrors = arg_parse(argc, argv, argtable);
146 
147     if (help->count > 0) {
148       log_fmt(
149           "Usage: %s [OPTION]...\n"
150           "Revoke Intel(R) EPID group\n"
151           "\n"
152           "Options:\n",
153           PROGRAM_NAME);
154       arg_print_glossary(stdout, argtable, "  %-25s %s\n");
155       ret_value = EXIT_SUCCESS;
156       break;
157     }
158     if (verbose->count > 0) {
159       verbose_flag = ToggleVerbosity();
160     }
161     /* If the parser returned any errors then display them and exit */
162     if (nerrors > 0) {
163       /* Display the error details contained in the arg_end struct.*/
164       arg_print_errors(stderr, end, PROGRAM_NAME);
165       fprintf(stderr, "Try '%s --help' for more information.\n", PROGRAM_NAME);
166       ret_value = EXIT_FAILURE;
167       break;
168     }
169     if (reason->ival[0] < 0 || reason->ival[0] > UCHAR_MAX) {
170       log_error(
171           "unexpected reason value. Value of the reason must be in a range "
172           "from 0 to %d",
173           UCHAR_MAX);
174       ret_value = EXIT_FAILURE;
175       break;
176     }
177     if (verbose_flag) {
178       log_msg("\nOption values:");
179       log_msg(" pubkey_file   : %s", pubkey_file->filename[0]);
180       log_msg(" cacert_file   : %s", cacert_file->filename[0]);
181       log_msg(" reason        : %d", reason->ival[0]);
182       log_msg(" req_file      : %s", req_file->filename[0]);
183       log_msg("");
184     }
185 
186     ret_value = MakeRequest(cacert_file->filename[0], pubkey_file->filename[0],
187                             req_file->filename[0], (uint8_t)reason->ival[0],
188                             verbose_flag);
189   } while (0);
190 
191   arg_freetable(argtable, sizeof(argtable) / sizeof(argtable[0]));
192 
193   return ret_value;
194 }
195 
MakeRequest(char const * cacert_file,char const * pubkey_file,char const * req_file,uint8_t reason,bool verbose)196 int MakeRequest(char const* cacert_file, char const* pubkey_file,
197                 char const* req_file, uint8_t reason, bool verbose) {
198   // Group index and count
199   uint32_t grp_index = 0;
200   uint32_t grp_count = 0;
201 
202   // Buffers and computed values
203   // Group public key file
204   unsigned char* pubkey_file_data = NULL;
205   size_t pubkey_file_size = 0;
206 
207   // Group public key buffer
208   GroupPubKey pubkey = {0};
209 
210   // CA certificate
211   EpidCaCertificate cacert = {0};
212 
213   // Request buffer
214   uint8_t* req_buf = NULL;
215   size_t req_size = 0;
216   size_t req_file_size = 0;
217   GrpRlRequest* request = NULL;
218   size_t req_extra_space = sizeof(GroupId) + sizeof(uint8_t);
219 
220   int ret_value = EXIT_FAILURE;
221   do {
222     if (!cacert_file || !pubkey_file || !req_file) {
223       log_error("internal error: badarg to MakeRequest()");
224       ret_value = EXIT_FAILURE;
225       break;
226     }
227 
228     // convert command line args to usable formats
229     // CA certificate
230     if (0 != ReadLoud(cacert_file, &cacert, sizeof(cacert))) {
231       ret_value = EXIT_FAILURE;
232       break;
233     }
234 
235     // Group public key file
236     pubkey_file_data = NewBufferFromFile(pubkey_file, &pubkey_file_size);
237     if (!pubkey_file_data) {
238       ret_value = EXIT_FAILURE;
239       break;
240     }
241 
242     // Security note:
243     // Application must confirm group public key is
244     // authorized by the issuer, e.g., signed by the issuer.
245     if (GROUP_PUB_KEY_SIZE != pubkey_file_size) {
246       log_error("unexpected file size for '%s'. Expected: %d; got: %d",
247                 pubkey_file, (int)GROUP_PUB_KEY_SIZE, pubkey_file_size);
248       ret_value = EXIT_FAILURE;
249       break;
250     }
251     if (kEpidNoErr != EpidParseGroupPubKeyFile(pubkey_file_data,
252                                                pubkey_file_size, &cacert,
253                                                &pubkey)) {
254       log_error("group public key is not authorized");
255       ret_value = EXIT_FAILURE;
256       break;
257     }
258 
259     // Report Settings
260     if (verbose) {
261       log_msg("==============================================");
262       log_msg("Input settings:");
263       log_msg("");
264       log_msg(" [in]  Group ID: ");
265       PrintBuffer(&pubkey.gid, sizeof(pubkey.gid));
266       log_msg("");
267       log_msg(" [in]  Reason: %d", reason);
268       log_msg("==============================================");
269     }
270 
271     // Calculate request size
272     req_size = sizeof(EpidFileHeader) + sizeof(uint32_t);
273 
274     if (FileExists(req_file)) {
275       req_file_size = GetFileSize_S(req_file, SIZE_MAX - req_extra_space);
276 
277       if (req_file_size < req_size) {
278         log_error("output file smaller then size of empty request");
279         ret_value = EXIT_FAILURE;
280         break;
281       }
282 
283       req_size = req_file_size;
284     } else {
285       log_msg("request file does not exsist, create new");
286     }
287 
288     req_size += req_extra_space;
289 
290     // Allocate request buffer
291     req_buf = AllocBuffer(req_size);
292     if (!req_buf) {
293       ret_value = EXIT_FAILURE;
294       break;
295     }
296 
297     request = (GrpRlRequest*)req_buf;
298 
299     // Load existing request file
300     if (req_file_size > 0) {
301       if (0 != ReadLoud(req_file, req_buf, req_file_size)) {
302         ret_value = EXIT_FAILURE;
303         break;
304       }
305 
306       // Check Intel(R) EPID and file versions
307       if (0 != memcmp(&request->header.epid_version, &kEpidFileVersion,
308                       sizeof(kEpidFileVersion))) {
309         ret_value = EXIT_FAILURE;
310         break;
311       }
312 
313       if (0 != memcmp(&request->header.file_type,
314                       &kEpidFileTypeCode[kGroupRlRequestFile],
315                       sizeof(kEpidFileTypeCode[kGroupRlRequestFile]))) {
316         ret_value = EXIT_FAILURE;
317         break;
318       }
319 
320       grp_count = ntohl(request->count);
321       // check if revoked count matches the number of group revocation request
322       // entries contained in the file
323       if (grp_count * sizeof(GrpInfo) !=
324           req_file_size - sizeof(EpidFileHeader) - sizeof(uint32_t)) {
325         log_error("Incorrect revoked request count in existing file");
326         ret_value = EXIT_FAILURE;
327         break;
328       }
329       // Update the reason if the group is in the request
330       for (grp_index = 0; grp_index < grp_count; grp_index++) {
331         if (0 == memcmp(&request->groups[grp_index].gid, &pubkey.gid,
332                         sizeof(pubkey.gid))) {
333           request->groups[grp_index].reason = reason;
334           req_size = req_file_size;
335           break;
336         }
337       }
338     }
339 
340     // Append group to the request
341     if (grp_index == grp_count) {
342       request->header.epid_version = kEpidFileVersion;
343       request->header.file_type = kEpidFileTypeCode[kGroupRlRequestFile];
344       request->groups[grp_count].gid = pubkey.gid;
345       request->groups[grp_count].reason = reason;
346       request->count = htonl(++grp_count);
347     }
348 
349     // Report Settings
350     if (verbose) {
351       log_msg("==============================================");
352       log_msg("Request generated:");
353       log_msg("");
354       log_msg(" [in]  Request Len: %d", (int)req_size);
355       log_msg(" [in]  Request: ");
356       PrintBuffer(req_buf, req_size);
357       log_msg("==============================================");
358     }
359 
360     // Store request
361     if (0 != WriteLoud(req_buf, req_size, req_file)) {
362       ret_value = EXIT_FAILURE;
363       break;
364     }
365 
366     // Success
367     ret_value = EXIT_SUCCESS;
368   } while (0);
369 
370   // Free allocated buffers
371   if (pubkey_file_data) free(pubkey_file_data);
372   if (req_buf) free(req_buf);
373 
374   return ret_value;
375 }
376