FreeRDP
makecert.c
1 
20 #include <errno.h>
21 
22 #include <winpr/assert.h>
23 #include <winpr/crt.h>
24 #include <winpr/path.h>
25 #include <winpr/file.h>
26 #include <winpr/cmdline.h>
27 #include <winpr/sysinfo.h>
28 #include <winpr/crypto.h>
29 
30 #ifdef WITH_OPENSSL
31 #include <openssl/crypto.h>
32 #include <openssl/conf.h>
33 #include <openssl/pem.h>
34 #include <openssl/err.h>
35 #include <openssl/rsa.h>
36 #include <openssl/pkcs12.h>
37 #include <openssl/x509v3.h>
38 #include <openssl/bn.h>
39 #endif
40 
41 #include <winpr/tools/makecert.h>
42 
43 struct S_MAKECERT_CONTEXT
44 {
45  int argc;
46  char** argv;
47 
48 #ifdef WITH_OPENSSL
49  X509* x509;
50  EVP_PKEY* pkey;
51  PKCS12* pkcs12;
52 #endif
53 
54  BOOL live;
55  BOOL silent;
56 
57  BOOL crtFormat;
58  BOOL pemFormat;
59  BOOL pfxFormat;
60 
61  char* password;
62 
63  char* output_file;
64  char* output_path;
65  char* default_name;
66  char* common_name;
67 
68  int duration_years;
69  int duration_months;
70 };
71 
72 static char* makecert_read_str(BIO* bio, size_t* pOffset)
73 {
74  int status = -1;
75  size_t offset = 0;
76  size_t length = 0;
77  char* x509_str = NULL;
78 
79  while (offset >= length)
80  {
81  size_t new_len = 0;
82  size_t readBytes = 0;
83  char* new_str = NULL;
84  new_len = length * 2;
85  if (new_len == 0)
86  new_len = 2048;
87 
88  if (new_len > INT_MAX)
89  {
90  status = -1;
91  break;
92  }
93 
94  new_str = (char*)realloc(x509_str, new_len);
95 
96  if (!new_str)
97  {
98  status = -1;
99  break;
100  }
101 
102  length = new_len;
103  x509_str = new_str;
104  ERR_clear_error();
105 #if OPENSSL_VERSION_NUMBER >= 0x10101000L && !defined(LIBRESSL_VERSION_NUMBER)
106  status = BIO_read_ex(bio, &x509_str[offset], length - offset, &readBytes);
107 #else
108  status = BIO_read(bio, &x509_str[offset], length - offset);
109  readBytes = status;
110 #endif
111  if (status <= 0)
112  break;
113 
114  offset += readBytes;
115  }
116 
117  if (status < 0)
118  {
119  free(x509_str);
120  if (pOffset)
121  *pOffset = 0;
122  return NULL;
123  }
124 
125  x509_str[offset] = '\0';
126  if (pOffset)
127  *pOffset = offset + 1;
128  return x509_str;
129 }
130 
131 static int makecert_print_command_line_help(COMMAND_LINE_ARGUMENT_A* args, int argc, char** argv)
132 {
133  char* str = NULL;
134  const COMMAND_LINE_ARGUMENT_A* arg = NULL;
135 
136  if (!argv || (argc < 1))
137  return -1;
138 
139  printf("Usage: %s [options] [output file]\n", argv[0]);
140  printf("\n");
141  arg = args;
142 
143  do
144  {
145  if (arg->Flags & COMMAND_LINE_VALUE_FLAG)
146  {
147  printf(" %s", "-");
148  printf("%-20s", arg->Name);
149  printf("\t%s\n", arg->Text);
150  }
151  else if ((arg->Flags & COMMAND_LINE_VALUE_REQUIRED) ||
152  (arg->Flags & COMMAND_LINE_VALUE_OPTIONAL))
153  {
154  printf(" %s", "-");
155 
156  if (arg->Format)
157  {
158  size_t length = strlen(arg->Name) + strlen(arg->Format) + 2;
159  str = malloc(length + 1);
160 
161  if (!str)
162  return -1;
163 
164  (void)sprintf_s(str, length + 1, "%s %s", arg->Name, arg->Format);
165  (void)printf("%-20s", str);
166  free(str);
167  }
168  else
169  {
170  printf("%-20s", arg->Name);
171  }
172 
173  printf("\t%s\n", arg->Text);
174  }
175  } while ((arg = CommandLineFindNextArgumentA(arg)) != NULL);
176 
177  return 1;
178 }
179 
180 #ifdef WITH_OPENSSL
181 static int x509_add_ext(X509* cert, int nid, char* value)
182 {
183  X509V3_CTX ctx;
184  X509_EXTENSION* ext = NULL;
185 
186  if (!cert || !value)
187  return 0;
188 
189  X509V3_set_ctx_nodb(&ctx) X509V3_set_ctx(&ctx, cert, cert, NULL, NULL, 0);
190  ext = X509V3_EXT_conf_nid(NULL, &ctx, nid, value);
191 
192  if (!ext)
193  return 0;
194 
195  X509_add_ext(cert, ext, -1);
196  X509_EXTENSION_free(ext);
197  return 1;
198 }
199 #endif
200 
201 static char* x509_name_parse(char* name, char* txt, size_t* length)
202 {
203  char* p = NULL;
204  char* entry = NULL;
205 
206  if (!name || !txt || !length)
207  return NULL;
208 
209  p = strstr(name, txt);
210 
211  if (!p)
212  return NULL;
213 
214  entry = p + strlen(txt) + 1;
215  p = strchr(entry, '=');
216 
217  if (!p)
218  *length = strlen(entry);
219  else
220  *length = (size_t)(p - entry);
221 
222  return entry;
223 }
224 
225 static char* get_name(COMPUTER_NAME_FORMAT type)
226 {
227  DWORD nSize = 0;
228 
229  if (GetComputerNameExA(type, NULL, &nSize))
230  return NULL;
231 
232  if (GetLastError() != ERROR_MORE_DATA)
233  return NULL;
234 
235  char* computerName = calloc(1, nSize);
236 
237  if (!computerName)
238  return NULL;
239 
240  if (!GetComputerNameExA(type, computerName, &nSize))
241  {
242  free(computerName);
243  return NULL;
244  }
245 
246  return computerName;
247 }
248 
249 static char* x509_get_default_name(void)
250 {
251  char* computerName = get_name(ComputerNamePhysicalDnsFullyQualified);
252  if (!computerName)
253  computerName = get_name(ComputerNamePhysicalNetBIOS);
254  return computerName;
255 }
256 
257 static int command_line_pre_filter(void* pvctx, int index, int argc, LPSTR* argv)
258 {
259  MAKECERT_CONTEXT* context = pvctx;
260  if (!context || !argv || (index < 0) || (argc < 0))
261  return -1;
262 
263  if (index == (argc - 1))
264  {
265  if (argv[index][0] != '-')
266  {
267  context->output_file = _strdup(argv[index]);
268 
269  if (!context->output_file)
270  return -1;
271 
272  return 1;
273  }
274  }
275 
276  return 0;
277 }
278 
279 static int makecert_context_parse_arguments(MAKECERT_CONTEXT* context,
280  COMMAND_LINE_ARGUMENT_A* args, int argc, char** argv)
281 {
282  int status = 0;
283  DWORD flags = 0;
284  const COMMAND_LINE_ARGUMENT_A* arg = NULL;
285 
286  if (!context || !argv || (argc < 0))
287  return -1;
288 
293  CommandLineClearArgumentsA(args);
294  flags = COMMAND_LINE_SEPARATOR_SPACE | COMMAND_LINE_SIGIL_DASH;
295  status =
296  CommandLineParseArgumentsA(argc, argv, args, flags, context, command_line_pre_filter, NULL);
297 
298  if (status & COMMAND_LINE_STATUS_PRINT_HELP)
299  {
300  makecert_print_command_line_help(args, argc, argv);
301  return 0;
302  }
303 
304  arg = args;
305  errno = 0;
306 
307  do
308  {
309  if (!(arg->Flags & COMMAND_LINE_ARGUMENT_PRESENT))
310  continue;
311 
312  CommandLineSwitchStart(arg)
313  /* Basic Options */
314  CommandLineSwitchCase(arg, "silent")
315  {
316  context->silent = TRUE;
317  }
318  CommandLineSwitchCase(arg, "live")
319  {
320  context->live = TRUE;
321  }
322  CommandLineSwitchCase(arg, "format")
323  {
324  if (!(arg->Flags & COMMAND_LINE_ARGUMENT_PRESENT))
325  continue;
326 
327  if (strcmp(arg->Value, "crt") == 0)
328  {
329  context->crtFormat = TRUE;
330  context->pemFormat = FALSE;
331  context->pfxFormat = FALSE;
332  }
333  else if (strcmp(arg->Value, "pem") == 0)
334  {
335  context->crtFormat = FALSE;
336  context->pemFormat = TRUE;
337  context->pfxFormat = FALSE;
338  }
339  else if (strcmp(arg->Value, "pfx") == 0)
340  {
341  context->crtFormat = FALSE;
342  context->pemFormat = FALSE;
343  context->pfxFormat = TRUE;
344  }
345  else
346  return -1;
347  }
348  CommandLineSwitchCase(arg, "path")
349  {
350  if (!(arg->Flags & COMMAND_LINE_ARGUMENT_PRESENT))
351  continue;
352 
353  context->output_path = _strdup(arg->Value);
354 
355  if (!context->output_path)
356  return -1;
357  }
358  CommandLineSwitchCase(arg, "p")
359  {
360  if (!(arg->Flags & COMMAND_LINE_ARGUMENT_PRESENT))
361  continue;
362 
363  context->password = _strdup(arg->Value);
364 
365  if (!context->password)
366  return -1;
367  }
368  CommandLineSwitchCase(arg, "n")
369  {
370  if (!(arg->Flags & COMMAND_LINE_ARGUMENT_PRESENT))
371  continue;
372 
373  context->common_name = _strdup(arg->Value);
374 
375  if (!context->common_name)
376  return -1;
377  }
378  CommandLineSwitchCase(arg, "y")
379  {
380  long val = 0;
381 
382  if (!(arg->Flags & COMMAND_LINE_ARGUMENT_PRESENT))
383  continue;
384 
385  val = strtol(arg->Value, NULL, 0);
386 
387  if ((errno != 0) || (val < 0) || (val > INT32_MAX))
388  return -1;
389 
390  context->duration_years = (int)val;
391  }
392  CommandLineSwitchCase(arg, "m")
393  {
394  long val = 0;
395 
396  if (!(arg->Flags & COMMAND_LINE_ARGUMENT_PRESENT))
397  continue;
398 
399  val = strtol(arg->Value, NULL, 0);
400 
401  if ((errno != 0) || (val < 0))
402  return -1;
403 
404  context->duration_months = (int)val;
405  }
406  CommandLineSwitchDefault(arg)
407  {
408  }
409  CommandLineSwitchEnd(arg)
410  } while ((arg = CommandLineFindNextArgumentA(arg)) != NULL);
411 
412  return 1;
413 }
414 
415 int makecert_context_set_output_file_name(MAKECERT_CONTEXT* context, const char* name)
416 {
417  if (!context)
418  return -1;
419 
420  free(context->output_file);
421  context->output_file = NULL;
422 
423  if (name)
424  context->output_file = _strdup(name);
425 
426  if (!context->output_file)
427  return -1;
428 
429  return 1;
430 }
431 
432 int makecert_context_output_certificate_file(MAKECERT_CONTEXT* context, const char* path)
433 {
434 #ifdef WITH_OPENSSL
435  FILE* fp = NULL;
436  int status = 0;
437  size_t length = 0;
438  size_t offset = 0;
439  char* filename = NULL;
440  char* fullpath = NULL;
441  char* ext = NULL;
442  int ret = -1;
443  BIO* bio = NULL;
444  char* x509_str = NULL;
445 
446  if (!context)
447  return -1;
448 
449  if (!context->output_file)
450  {
451  context->output_file = _strdup(context->default_name);
452 
453  if (!context->output_file)
454  return -1;
455  }
456 
457  /*
458  * Output Certificate File
459  */
460  length = strlen(context->output_file);
461  filename = malloc(length + 8);
462 
463  if (!filename)
464  return -1;
465 
466  if (context->crtFormat)
467  ext = "crt";
468  else if (context->pemFormat)
469  ext = "pem";
470  else if (context->pfxFormat)
471  ext = "pfx";
472  else
473  goto out_fail;
474 
475  (void)sprintf_s(filename, length + 8, "%s.%s", context->output_file, ext);
476 
477  if (path)
478  fullpath = GetCombinedPath(path, filename);
479  else
480  fullpath = _strdup(filename);
481 
482  if (!fullpath)
483  goto out_fail;
484 
485  fp = winpr_fopen(fullpath, "w+");
486 
487  if (fp)
488  {
489  if (context->pfxFormat)
490  {
491  if (!context->password)
492  {
493  context->password = _strdup("password");
494 
495  if (!context->password)
496  goto out_fail;
497 
498  printf("Using default export password \"password\"\n");
499  }
500 
501 #if OPENSSL_VERSION_NUMBER < 0x10100000L || defined(LIBRESSL_VERSION_NUMBER)
502  OpenSSL_add_all_algorithms();
503  OpenSSL_add_all_ciphers();
504  OpenSSL_add_all_digests();
505 #else
506  OPENSSL_init_crypto(OPENSSL_INIT_ADD_ALL_CIPHERS | OPENSSL_INIT_ADD_ALL_DIGESTS |
507  OPENSSL_INIT_LOAD_CONFIG,
508  NULL);
509 #endif
510  context->pkcs12 = PKCS12_create(context->password, context->default_name, context->pkey,
511  context->x509, NULL, 0, 0, 0, 0, 0);
512 
513  if (!context->pkcs12)
514  goto out_fail;
515 
516  bio = BIO_new(BIO_s_mem());
517 
518  if (!bio)
519  goto out_fail;
520 
521  status = i2d_PKCS12_bio(bio, context->pkcs12);
522 
523  if (status != 1)
524  goto out_fail;
525 
526  x509_str = makecert_read_str(bio, &offset);
527 
528  if (!x509_str)
529  goto out_fail;
530 
531  length = offset;
532 
533  if (fwrite((void*)x509_str, length, 1, fp) != 1)
534  goto out_fail;
535  }
536  else
537  {
538  bio = BIO_new(BIO_s_mem());
539 
540  if (!bio)
541  goto out_fail;
542 
543  if (!PEM_write_bio_X509(bio, context->x509))
544  goto out_fail;
545 
546  x509_str = makecert_read_str(bio, &offset);
547 
548  if (!x509_str)
549  goto out_fail;
550 
551  length = offset;
552 
553  if (fwrite(x509_str, length, 1, fp) != 1)
554  goto out_fail;
555 
556  free(x509_str);
557  x509_str = NULL;
558  BIO_free_all(bio);
559  bio = NULL;
560 
561  if (context->pemFormat)
562  {
563  bio = BIO_new(BIO_s_mem());
564 
565  if (!bio)
566  goto out_fail;
567 
568  status = PEM_write_bio_PrivateKey(bio, context->pkey, NULL, NULL, 0, NULL, NULL);
569 
570  if (status < 0)
571  goto out_fail;
572 
573  x509_str = makecert_read_str(bio, &offset);
574  if (!x509_str)
575  goto out_fail;
576 
577  length = offset;
578 
579  if (fwrite(x509_str, length, 1, fp) != 1)
580  goto out_fail;
581  }
582  }
583  }
584 
585  ret = 1;
586 out_fail:
587  BIO_free_all(bio);
588 
589  if (fp)
590  (void)fclose(fp);
591 
592  free(x509_str);
593  free(filename);
594  free(fullpath);
595  return ret;
596 #else
597  WLog_ERR(TAG, "%s only supported with OpenSSL", __func__);
598  return -1;
599 #endif
600 }
601 
602 int makecert_context_output_private_key_file(MAKECERT_CONTEXT* context, const char* path)
603 {
604 #ifdef WITH_OPENSSL
605  FILE* fp = NULL;
606  size_t length = 0;
607  size_t offset = 0;
608  char* filename = NULL;
609  char* fullpath = NULL;
610  int ret = -1;
611  BIO* bio = NULL;
612  char* x509_str = NULL;
613 
614  if (!context->crtFormat)
615  return 1;
616 
617  if (!context->output_file)
618  {
619  context->output_file = _strdup(context->default_name);
620 
621  if (!context->output_file)
622  return -1;
623  }
624 
628  length = strlen(context->output_file);
629  filename = malloc(length + 8);
630 
631  if (!filename)
632  return -1;
633 
634  (void)sprintf_s(filename, length + 8, "%s.key", context->output_file);
635 
636  if (path)
637  fullpath = GetCombinedPath(path, filename);
638  else
639  fullpath = _strdup(filename);
640 
641  if (!fullpath)
642  goto out_fail;
643 
644  fp = winpr_fopen(fullpath, "w+");
645 
646  if (!fp)
647  goto out_fail;
648 
649  bio = BIO_new(BIO_s_mem());
650 
651  if (!bio)
652  goto out_fail;
653 
654  if (!PEM_write_bio_PrivateKey(bio, context->pkey, NULL, NULL, 0, NULL, NULL))
655  goto out_fail;
656 
657  x509_str = makecert_read_str(bio, &offset);
658 
659  if (!x509_str)
660  goto out_fail;
661 
662  length = offset;
663 
664  if (fwrite((void*)x509_str, length, 1, fp) != 1)
665  goto out_fail;
666 
667  ret = 1;
668 out_fail:
669 
670  if (fp)
671  (void)fclose(fp);
672 
673  BIO_free_all(bio);
674  free(x509_str);
675  free(filename);
676  free(fullpath);
677  return ret;
678 #else
679  WLog_ERR(TAG, "%s only supported with OpenSSL", __func__);
680  return -1;
681 #endif
682 }
683 
684 #ifdef WITH_OPENSSL
685 static BOOL makecert_create_rsa(EVP_PKEY** ppkey, size_t key_length)
686 {
687  BOOL rc = FALSE;
688 
689  WINPR_ASSERT(ppkey);
690 
691 #if !defined(OPENSSL_VERSION_MAJOR) || (OPENSSL_VERSION_MAJOR < 3)
692  RSA* rsa = NULL;
693 #if (OPENSSL_VERSION_NUMBER < 0x10100000L) || defined(LIBRESSL_VERSION_NUMBER)
694  rsa = RSA_generate_key(key_length, RSA_F4, NULL, NULL);
695 #else
696  {
697  BIGNUM* bn = BN_secure_new();
698 
699  if (!bn)
700  return FALSE;
701 
702  rsa = RSA_new();
703 
704  if (!rsa)
705  {
706  BN_clear_free(bn);
707  return FALSE;
708  }
709 
710  BN_set_word(bn, RSA_F4);
711  const int res = RSA_generate_key_ex(rsa, key_length, bn, NULL);
712  BN_clear_free(bn);
713 
714  if (res != 1)
715  return FALSE;
716  }
717 #endif
718 
719  if (!EVP_PKEY_assign_RSA(*ppkey, rsa))
720  {
721  RSA_free(rsa);
722  return FALSE;
723  }
724  rc = TRUE;
725 #else
726  EVP_PKEY_CTX* pctx = EVP_PKEY_CTX_new_from_name(NULL, "RSA", NULL);
727  if (!pctx)
728  return FALSE;
729 
730  if (EVP_PKEY_keygen_init(pctx) != 1)
731  goto fail;
732 
733  WINPR_ASSERT(key_length <= UINT_MAX);
734  unsigned int keylen = (unsigned int)key_length;
735  const OSSL_PARAM params[] = { OSSL_PARAM_construct_uint("bits", &keylen),
736  OSSL_PARAM_construct_end() };
737  if (EVP_PKEY_CTX_set_params(pctx, params) != 1)
738  goto fail;
739 
740  if (EVP_PKEY_generate(pctx, ppkey) != 1)
741  goto fail;
742 
743  rc = TRUE;
744 fail:
745  EVP_PKEY_CTX_free(pctx);
746 #endif
747  return rc;
748 }
749 #endif
750 
751 int makecert_context_process(MAKECERT_CONTEXT* context, int argc, char** argv)
752 {
753  COMMAND_LINE_ARGUMENT_A args[] = {
754  /* Custom Options */
755 
756  { "rdp", COMMAND_LINE_VALUE_FLAG, NULL, NULL, NULL, -1, NULL,
757  "Unsupported - Generate certificate with required options for RDP usage." },
758  { "silent", COMMAND_LINE_VALUE_FLAG, NULL, NULL, NULL, -1, NULL,
759  "Silently generate certificate without verbose output." },
760  { "live", COMMAND_LINE_VALUE_FLAG, NULL, NULL, NULL, -1, NULL,
761  "Generate certificate live in memory when used as a library." },
762  { "format", COMMAND_LINE_VALUE_REQUIRED, "<crt|pem|pfx>", NULL, NULL, -1, NULL,
763  "Specify certificate file format" },
764  { "path", COMMAND_LINE_VALUE_REQUIRED, "<path>", NULL, NULL, -1, NULL,
765  "Specify certificate file output path" },
766  { "p", COMMAND_LINE_VALUE_REQUIRED, "<password>", NULL, NULL, -1, NULL,
767  "Specify certificate export password" },
768 
769  /* Basic Options */
770 
771  { "n", COMMAND_LINE_VALUE_REQUIRED, "<name>", NULL, NULL, -1, NULL,
772  "Specifies the subject's certificate name. This name must conform to the X.500 standard. "
773  "The simplest method is to specify the name in double quotes, preceded by CN=; for "
774  "example, "
775  "-n \"CN=myName\"." },
776  { "pe", COMMAND_LINE_VALUE_FLAG, NULL, NULL, NULL, -1, NULL,
777  "Unsupported - Marks the generated private key as exportable. This allows the private "
778  "key to "
779  "be included in the certificate." },
780  { "sk", COMMAND_LINE_VALUE_REQUIRED, "<keyname>", NULL, NULL, -1, NULL,
781  "Unsupported - Specifies the subject's key container location, which contains the "
782  "private "
783  "key. "
784  "If a key container does not exist, it will be created." },
785  { "sr", COMMAND_LINE_VALUE_REQUIRED, "<location>", NULL, NULL, -1, NULL,
786  "Unsupported - Specifies the subject's certificate store location. location can be "
787  "either "
788  "currentuser (the default) or localmachine." },
789  { "ss", COMMAND_LINE_VALUE_REQUIRED, "<store>", NULL, NULL, -1, NULL,
790  "Unsupported - Specifies the subject's certificate store name that stores the output "
791  "certificate." },
792  { "#", COMMAND_LINE_VALUE_REQUIRED, "<number>", NULL, NULL, -1, NULL,
793  "Specifies a serial number from 1 to 2,147,483,647. The default is a unique value "
794  "generated "
795  "by Makecert.exe." },
796  { "$", COMMAND_LINE_VALUE_REQUIRED, "<authority>", NULL, NULL, -1, NULL,
797  "Unsupported - Specifies the signing authority of the certificate, which must be set to "
798  "either commercial "
799  "(for certificates used by commercial software publishers) or individual (for "
800  "certificates "
801  "used by individual software publishers)." },
802 
803  /* Extended Options */
804 
805  { "a", COMMAND_LINE_VALUE_REQUIRED, "<algorithm>", NULL, NULL, -1, NULL,
806  "Specifies the signature algorithm. algorithm must be md5, sha1, sha256 (the default), "
807  "sha384, or sha512." },
808  { "b", COMMAND_LINE_VALUE_REQUIRED, "<mm/dd/yyyy>", NULL, NULL, -1, NULL,
809  "Unsupported - Specifies the start of the validity period. Defaults to the current "
810  "date." },
811  { "crl", COMMAND_LINE_VALUE_FLAG, NULL, NULL, NULL, -1, NULL,
812  "Unsupported - Generates a certificate relocation list (CRL) instead of a certificate." },
813  { "cy", COMMAND_LINE_VALUE_REQUIRED, "<certType>", NULL, NULL, -1, NULL,
814  "Unsupported - Specifies the certificate type. Valid values are end for end-entity and "
815  "authority for certification authority." },
816  { "e", COMMAND_LINE_VALUE_REQUIRED, "<mm/dd/yyyy>", NULL, NULL, -1, NULL,
817  "Unsupported - Specifies the end of the validity period. Defaults to 12/31/2039 11:59:59 "
818  "GMT." },
819  { "eku", COMMAND_LINE_VALUE_REQUIRED, "<oid[,oid…]>", NULL, NULL, -1, NULL,
820  "Unsupported - Inserts a list of comma-separated, enhanced key usage object identifiers "
821  "(OIDs) into the certificate." },
822  { "h", COMMAND_LINE_VALUE_REQUIRED, "<number>", NULL, NULL, -1, NULL,
823  "Unsupported - Specifies the maximum height of the tree below this certificate." },
824  { "ic", COMMAND_LINE_VALUE_REQUIRED, "<file>", NULL, NULL, -1, NULL,
825  "Unsupported - Specifies the issuer's certificate file." },
826  { "ik", COMMAND_LINE_VALUE_REQUIRED, "<keyName>", NULL, NULL, -1, NULL,
827  "Unsupported - Specifies the issuer's key container name." },
828  { "iky", COMMAND_LINE_VALUE_REQUIRED, "<keyType>", NULL, NULL, -1, NULL,
829  "Unsupported - Specifies the issuer's key type, which must be one of the following: "
830  "signature (which indicates that the key is used for a digital signature), "
831  "exchange (which indicates that the key is used for key encryption and key exchange), "
832  "or an integer that represents a provider type. "
833  "By default, you can pass 1 for an exchange key or 2 for a signature key." },
834  { "in", COMMAND_LINE_VALUE_REQUIRED, "<name>", NULL, NULL, -1, NULL,
835  "Unsupported - Specifies the issuer's certificate common name." },
836  { "ip", COMMAND_LINE_VALUE_REQUIRED, "<provider>", NULL, NULL, -1, NULL,
837  "Unsupported - Specifies the issuer's CryptoAPI provider name. For information about the "
838  "CryptoAPI provider name, see the –sp option." },
839  { "ir", COMMAND_LINE_VALUE_REQUIRED, "<location>", NULL, NULL, -1, NULL,
840  "Unsupported - Specifies the location of the issuer's certificate store. location can be "
841  "either currentuser (the default) or localmachine." },
842  { "is", COMMAND_LINE_VALUE_REQUIRED, "<store>", NULL, NULL, -1, NULL,
843  "Unsupported - Specifies the issuer's certificate store name." },
844  { "iv", COMMAND_LINE_VALUE_REQUIRED, "<pvkFile>", NULL, NULL, -1, NULL,
845  "Unsupported - Specifies the issuer's .pvk private key file." },
846  { "iy", COMMAND_LINE_VALUE_REQUIRED, "<type>", NULL, NULL, -1, NULL,
847  "Unsupported - Specifies the issuer's CryptoAPI provider type. For information about the "
848  "CryptoAPI provider type, see the –sy option." },
849  { "l", COMMAND_LINE_VALUE_REQUIRED, "<link>", NULL, NULL, -1, NULL,
850  "Unsupported - Links to policy information (for example, to a URL)." },
851  { "len", COMMAND_LINE_VALUE_REQUIRED, "<number>", NULL, NULL, -1, NULL,
852  "Specifies the generated key length, in bits." },
853  { "m", COMMAND_LINE_VALUE_REQUIRED, "<number>", NULL, NULL, -1, NULL,
854  "Specifies the duration, in months, of the certificate validity period." },
855  { "y", COMMAND_LINE_VALUE_REQUIRED, "<number>", NULL, NULL, -1, NULL,
856  "Specifies the duration, in years, of the certificate validity period." },
857  { "nscp", COMMAND_LINE_VALUE_FLAG, NULL, NULL, NULL, -1, NULL,
858  "Unsupported - Includes the Netscape client-authorization extension." },
859  { "r", COMMAND_LINE_VALUE_FLAG, NULL, NULL, NULL, -1, NULL,
860  "Unsupported - Creates a self-signed certificate." },
861  { "sc", COMMAND_LINE_VALUE_REQUIRED, "<file>", NULL, NULL, -1, NULL,
862  "Unsupported - Specifies the subject's certificate file." },
863  { "sky", COMMAND_LINE_VALUE_REQUIRED, "<keyType>", NULL, NULL, -1, NULL,
864  "Unsupported - Specifies the subject's key type, which must be one of the following: "
865  "signature (which indicates that the key is used for a digital signature), "
866  "exchange (which indicates that the key is used for key encryption and key exchange), "
867  "or an integer that represents a provider type. "
868  "By default, you can pass 1 for an exchange key or 2 for a signature key." },
869  { "sp", COMMAND_LINE_VALUE_REQUIRED, "<provider>", NULL, NULL, -1, NULL,
870  "Unsupported - Specifies the subject's CryptoAPI provider name, which must be defined in "
871  "the "
872  "registry subkeys of "
873  "HKEY_LOCAL_MACHINE\\SOFTWARE\\Microsoft\\Cryptography\\Defaults\\Provider. If both –sp "
874  "and "
875  "–sy are present, "
876  "the type of the CryptoAPI provider must correspond to the Type value of the provider's "
877  "subkey." },
878  { "sv", COMMAND_LINE_VALUE_REQUIRED, "<pvkFile>", NULL, NULL, -1, NULL,
879  "Unsupported - Specifies the subject's .pvk private key file. The file is created if "
880  "none "
881  "exists." },
882  { "sy", COMMAND_LINE_VALUE_REQUIRED, "<type>", NULL, NULL, -1, NULL,
883  "Unsupported - Specifies the subject's CryptoAPI provider type, which must be defined in "
884  "the "
885  "registry subkeys of "
886  "HKEY_LOCAL_MACHINE\\SOFTWARE\\Microsoft\\Cryptography\\Defaults\\Provider Types. If "
887  "both "
888  "–sy and –sp are present, "
889  "the name of the CryptoAPI provider must correspond to the Name value of the provider "
890  "type "
891  "subkey." },
892  { "tbs", COMMAND_LINE_VALUE_REQUIRED, "<file>", NULL, NULL, -1, NULL,
893  "Unsupported - Specifies the certificate or CRL file to be signed." },
894 
895  /* Help */
896 
897  { "?", COMMAND_LINE_VALUE_FLAG | COMMAND_LINE_PRINT_HELP, NULL, NULL, NULL, -1, "help",
898  "print help" },
899  { "!", COMMAND_LINE_VALUE_FLAG | COMMAND_LINE_PRINT_HELP, NULL, NULL, NULL, -1, "help-ext",
900  "print extended help" },
901  { NULL, 0, NULL, NULL, NULL, -1, NULL, NULL }
902  };
903 #ifdef WITH_OPENSSL
904  size_t length = 0;
905  char* entry = NULL;
906  int key_length = 0;
907  long serial = 0;
908  X509_NAME* name = NULL;
909  const EVP_MD* md = NULL;
910  const COMMAND_LINE_ARGUMENT_A* arg = NULL;
911  int ret = 0;
912  ret = makecert_context_parse_arguments(context, args, argc, argv);
913 
914  if (ret < 1)
915  {
916  return ret;
917  }
918 
919  if (!context->default_name && !context->common_name)
920  {
921  context->default_name = x509_get_default_name();
922 
923  if (!context->default_name)
924  return -1;
925  }
926  else
927  {
928  context->default_name = _strdup(context->common_name);
929 
930  if (!context->default_name)
931  return -1;
932  }
933 
934  if (!context->common_name)
935  {
936  context->common_name = _strdup(context->default_name);
937 
938  if (!context->common_name)
939  return -1;
940  }
941 
942  if (!context->pkey)
943  context->pkey = EVP_PKEY_new();
944 
945  if (!context->pkey)
946  return -1;
947 
948  if (!context->x509)
949  context->x509 = X509_new();
950 
951  if (!context->x509)
952  return -1;
953 
954  key_length = 2048;
955  arg = CommandLineFindArgumentA(args, "len");
956 
957  if (arg->Flags & COMMAND_LINE_VALUE_PRESENT)
958  {
959  unsigned long val = strtoul(arg->Value, NULL, 0);
960 
961  if ((errno != 0) || (val > INT_MAX))
962  return -1;
963  key_length = (int)val;
964  }
965 
966  if (!makecert_create_rsa(&context->pkey, WINPR_ASSERTING_INT_CAST(size_t, key_length)))
967  return -1;
968 
969  X509_set_version(context->x509, 2);
970  arg = CommandLineFindArgumentA(args, "#");
971 
972  if (arg->Flags & COMMAND_LINE_VALUE_PRESENT)
973  {
974  serial = strtol(arg->Value, NULL, 0);
975 
976  if (errno != 0)
977  return -1;
978  }
979  else
980  serial = (long)GetTickCount64();
981 
982  ASN1_INTEGER_set(X509_get_serialNumber(context->x509), serial);
983  {
984  ASN1_TIME* before = NULL;
985  ASN1_TIME* after = NULL;
986 #if (OPENSSL_VERSION_NUMBER < 0x10100000L) || defined(LIBRESSL_VERSION_NUMBER)
987  before = X509_get_notBefore(context->x509);
988  after = X509_get_notAfter(context->x509);
989 #else
990  before = X509_getm_notBefore(context->x509);
991  after = X509_getm_notAfter(context->x509);
992 #endif
993  X509_gmtime_adj(before, 0);
994 
995  long duration = context->duration_months * 31l + context->duration_years * 365l;
996  duration *= 60l * 60l * 24l;
997  X509_gmtime_adj(after, duration);
998  }
999  X509_set_pubkey(context->x509, context->pkey);
1000  name = X509_get_subject_name(context->x509);
1001  arg = CommandLineFindArgumentA(args, "n");
1002 
1003  if (arg->Flags & COMMAND_LINE_VALUE_PRESENT)
1004  {
1005  entry = x509_name_parse(arg->Value, "C", &length);
1006 
1007  if (entry)
1008  X509_NAME_add_entry_by_txt(name, "C", MBSTRING_UTF8, (const unsigned char*)entry,
1009  (int)length, -1, 0);
1010 
1011  entry = x509_name_parse(arg->Value, "ST", &length);
1012 
1013  if (entry)
1014  X509_NAME_add_entry_by_txt(name, "ST", MBSTRING_UTF8, (const unsigned char*)entry,
1015  (int)length, -1, 0);
1016 
1017  entry = x509_name_parse(arg->Value, "L", &length);
1018 
1019  if (entry)
1020  X509_NAME_add_entry_by_txt(name, "L", MBSTRING_UTF8, (const unsigned char*)entry,
1021  (int)length, -1, 0);
1022 
1023  entry = x509_name_parse(arg->Value, "O", &length);
1024 
1025  if (entry)
1026  X509_NAME_add_entry_by_txt(name, "O", MBSTRING_UTF8, (const unsigned char*)entry,
1027  (int)length, -1, 0);
1028 
1029  entry = x509_name_parse(arg->Value, "OU", &length);
1030 
1031  if (entry)
1032  X509_NAME_add_entry_by_txt(name, "OU", MBSTRING_UTF8, (const unsigned char*)entry,
1033  (int)length, -1, 0);
1034 
1035  entry = context->common_name;
1036  length = strlen(entry);
1037  X509_NAME_add_entry_by_txt(name, "CN", MBSTRING_UTF8, (const unsigned char*)entry,
1038  (int)length, -1, 0);
1039  }
1040  else
1041  {
1042  entry = context->common_name;
1043  length = strlen(entry);
1044  X509_NAME_add_entry_by_txt(name, "CN", MBSTRING_UTF8, (const unsigned char*)entry,
1045  (int)length, -1, 0);
1046  }
1047 
1048  X509_set_issuer_name(context->x509, name);
1049  x509_add_ext(context->x509, NID_ext_key_usage, "serverAuth");
1050  arg = CommandLineFindArgumentA(args, "a");
1051  md = EVP_sha256();
1052 
1053  if (arg->Flags & COMMAND_LINE_VALUE_PRESENT)
1054  {
1055  md = EVP_get_digestbyname(arg->Value);
1056  if (!md)
1057  return -1;
1058  }
1059 
1060  if (!X509_sign(context->x509, context->pkey, md))
1061  return -1;
1062 
1067  if (!context->silent)
1068  {
1069  BIO* bio = NULL;
1070  int status = 0;
1071  char* x509_str = NULL;
1072  bio = BIO_new(BIO_s_mem());
1073 
1074  if (!bio)
1075  return -1;
1076 
1077  status = X509_print(bio, context->x509);
1078 
1079  if (status < 0)
1080  {
1081  BIO_free_all(bio);
1082  return -1;
1083  }
1084 
1085  x509_str = makecert_read_str(bio, NULL);
1086  if (!x509_str)
1087  {
1088  BIO_free_all(bio);
1089  return -1;
1090  }
1091 
1092  printf("%s", x509_str);
1093  free(x509_str);
1094  BIO_free_all(bio);
1095  }
1096 
1101  if (!context->live)
1102  {
1103  if (!winpr_PathFileExists(context->output_path))
1104  {
1105  if (!CreateDirectoryA(context->output_path, NULL))
1106  return -1;
1107  }
1108 
1109  if (makecert_context_output_certificate_file(context, context->output_path) != 1)
1110  return -1;
1111 
1112  if (context->crtFormat)
1113  {
1114  if (makecert_context_output_private_key_file(context, context->output_path) < 0)
1115  return -1;
1116  }
1117  }
1118 
1119  return 0;
1120 #else
1121  WLog_ERR(TAG, "%s only supported with OpenSSL", __func__);
1122  return -1;
1123 #endif
1124 }
1125 
1126 MAKECERT_CONTEXT* makecert_context_new(void)
1127 {
1128  MAKECERT_CONTEXT* context = (MAKECERT_CONTEXT*)calloc(1, sizeof(MAKECERT_CONTEXT));
1129 
1130  if (context)
1131  {
1132  context->crtFormat = TRUE;
1133  context->duration_years = 1;
1134  }
1135 
1136  return context;
1137 }
1138 
1139 void makecert_context_free(MAKECERT_CONTEXT* context)
1140 {
1141  if (context)
1142  {
1143  free(context->password);
1144  free(context->default_name);
1145  free(context->common_name);
1146  free(context->output_file);
1147  free(context->output_path);
1148 #ifdef WITH_OPENSSL
1149  X509_free(context->x509);
1150  EVP_PKEY_free(context->pkey);
1151 #if (OPENSSL_VERSION_NUMBER < 0x10100000L) || defined(LIBRESSL_VERSION_NUMBER)
1152  CRYPTO_cleanup_all_ex_data();
1153 #endif
1154 #endif
1155  free(context);
1156  }
1157 }