/** * Copyright (C) 2021 Graham Leggett * * Licensed under the Apache License, Version 2.0 (the "License"); * you may not use this file except in compliance with the License. * You may obtain a copy of the License at * * http://www.apache.org/licenses/LICENSE-2.0 * * Unless required by applicable law or agreed to in writing, software * distributed under the License is distributed on an "AS IS" BASIS, * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. * See the License for the specific language governing permissions and * limitations under the License. * */ /* * redwax-tool - the redwax certificate munching tool * */ #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include "config.h" #include "redwax-tool.h" #include "redwax_util.h" #include "redwax_nss.h" #include "redwax_openssl.h" #include "redwax_p11kit.h" #include "redwax_libical.h" #include "redwax_keychain.h" #if HAVE_LIBGEN_H #include #endif extern module core_module; extern module openssl_module; extern module nss_module; extern module p11kit_module; extern module libical_module; extern module keychain_module; extern module ldns_module; extern module unbound_module; module *redwax_modules[] = { &core_module, &openssl_module, &nss_module, &p11kit_module, &libical_module, &keychain_module, &ldns_module, &unbound_module, NULL }; module core_module; APR_HOOK_STRUCT( APR_HOOK_LINK(initialise); APR_HOOK_LINK(set_verify_param); APR_HOOK_LINK(complete_verify_param); APR_HOOK_LINK(set_verify_date); APR_HOOK_LINK(set_verify_expiry); APR_HOOK_LINK(process_pem_in); APR_HOOK_LINK(process_trust_pem_in); APR_HOOK_LINK(complete_pkcs11_in); APR_HOOK_LINK(process_pkcs11_in); APR_HOOK_LINK(complete_pkcs11_module_in); APR_HOOK_LINK(process_pkcs11_module_in); APR_HOOK_LINK(process_pkcs12_in); APR_HOOK_LINK(complete_keychain_in); APR_HOOK_LINK(process_keychain_in); APR_HOOK_LINK(complete_filter); APR_HOOK_LINK(process_filter); APR_HOOK_LINK(complete_nss_out); APR_HOOK_LINK(process_nss_out); APR_HOOK_LINK(complete_nss_token_out); APR_HOOK_LINK(complete_der_out); APR_HOOK_LINK(process_der_out); APR_HOOK_LINK(complete_pem_out); APR_HOOK_LINK(process_pem_out); APR_HOOK_LINK(complete_pkcs12_out); APR_HOOK_LINK(process_pkcs12_out); APR_HOOK_LINK(complete_pkcs11_out); APR_HOOK_LINK(process_pkcs11_out); APR_HOOK_LINK(complete_pkcs11_module_out); APR_HOOK_LINK(process_pkcs11_module_out); APR_HOOK_LINK(process_metadata_out); APR_HOOK_LINK(process_calendar_out); APR_HOOK_LINK(process_reminder_out); APR_HOOK_LINK(process_ssh_public_out); APR_HOOK_LINK(complete_format_out); APR_HOOK_LINK(process_jwks_out); APR_HOOK_LINK(set_format_out); APR_HOOK_LINK(complete_order_out); APR_HOOK_LINK(set_order_out); APR_HOOK_LINK(set_calendar_alarm); APR_HOOK_LINK(search_chain); APR_HOOK_LINK(search_key); APR_HOOK_LINK(compare_certificate); APR_HOOK_LINK(normalise_key); APR_HOOK_LINK(normalise_certificate); APR_HOOK_LINK(add_dns_metadata); ); APR_IMPLEMENT_EXTERNAL_HOOK_RUN_ALL(rt, REDWAX, int, initialise, (redwax_tool_t * r), (r), OK, DECLINED); APR_IMPLEMENT_EXTERNAL_HOOK_RUN_ALL(rt, REDWAX, int, set_verify_param, (redwax_tool_t * r, const char *arg), (r, arg), OK, DECLINED); APR_IMPLEMENT_EXTERNAL_HOOK_RUN_ALL(rt, REDWAX, int, complete_verify_param, (redwax_tool_t * r, apr_hash_t *params), (r, params), OK, DECLINED); APR_IMPLEMENT_EXTERNAL_HOOK_RUN_ALL(rt, REDWAX, int, set_verify_date, (redwax_tool_t * r, const char *arg), (r, arg), OK, DECLINED); APR_IMPLEMENT_EXTERNAL_HOOK_RUN_ALL(rt, REDWAX, int, set_verify_expiry, (redwax_tool_t * r, const char *arg), (r, arg), OK, DECLINED); APR_IMPLEMENT_EXTERNAL_HOOK_RUN_FIRST(rt, REDWAX, int, process_pem_in, (redwax_tool_t * r, const char *arg, const char *secret), (r, arg, secret), DECLINED); APR_IMPLEMENT_EXTERNAL_HOOK_RUN_FIRST(rt, REDWAX, int, process_trust_pem_in, (redwax_tool_t * r, const char *arg, const char *secret), (r, arg, secret), DECLINED); APR_IMPLEMENT_EXTERNAL_HOOK_RUN_FIRST(rt, REDWAX, int, process_pkcs11_in, (redwax_tool_t * r, const char *arg, apr_hash_t *secrets), (r, arg, secrets), DECLINED); APR_IMPLEMENT_EXTERNAL_HOOK_RUN_FIRST(rt, REDWAX, int, complete_pkcs11_in, (redwax_tool_t * r, const char *url, apr_hash_t *urls), (r, url, urls), DECLINED); APR_IMPLEMENT_EXTERNAL_HOOK_RUN_FIRST(rt, REDWAX, int, process_pkcs11_module_in, (redwax_tool_t * r, const char *arg), (r, arg), DECLINED); APR_IMPLEMENT_EXTERNAL_HOOK_RUN_FIRST(rt, REDWAX, int, complete_pkcs11_module_in, (redwax_tool_t * r, const char *mod, redwax_token_quoted_e quoted), (r, mod, quoted), DECLINED); APR_IMPLEMENT_EXTERNAL_HOOK_RUN_FIRST(rt, REDWAX, int, process_pkcs12_in, (redwax_tool_t * r, const char *arg, const char *secret), (r, arg, secret), DECLINED); APR_IMPLEMENT_EXTERNAL_HOOK_RUN_FIRST(rt, REDWAX, int, process_keychain_in, (redwax_tool_t * r, const char *arg), (r, arg), DECLINED); APR_IMPLEMENT_EXTERNAL_HOOK_RUN_FIRST(rt, REDWAX, int, complete_keychain_in, (redwax_tool_t * r, const char *url, apr_hash_t *urls), (r, url, urls), DECLINED); APR_IMPLEMENT_EXTERNAL_HOOK_RUN_ALL(rt, REDWAX, int, complete_filter, (redwax_tool_t * r, apr_hash_t *filters), (r, filters), OK, DECLINED); APR_IMPLEMENT_EXTERNAL_HOOK_RUN_FIRST(rt, REDWAX, int, process_filter, (redwax_tool_t * r, const char *arg), (r, arg), DECLINED); APR_IMPLEMENT_EXTERNAL_HOOK_RUN_FIRST(rt, REDWAX, int, process_nss_out, (redwax_tool_t * r, const char *path, const char *token, apr_hash_t *secrets), (r, path, token, secrets), DECLINED); APR_IMPLEMENT_EXTERNAL_HOOK_RUN_FIRST(rt, REDWAX, int, complete_nss_token_out, (redwax_tool_t * r, apr_hash_t *tokens), (r, tokens), DECLINED); APR_IMPLEMENT_EXTERNAL_HOOK_RUN_FIRST(rt, REDWAX, int, process_der_out, (redwax_tool_t * r, const char *arg, const char *secret), (r, arg, secret), DECLINED); APR_IMPLEMENT_EXTERNAL_HOOK_RUN_FIRST(rt, REDWAX, int, process_pem_out, (redwax_tool_t * r, const char *arg, const char *secret), (r, arg, secret), DECLINED); APR_IMPLEMENT_EXTERNAL_HOOK_RUN_FIRST(rt, REDWAX, int, process_pkcs12_out, (redwax_tool_t * r, const char *arg, const char *secret), (r, arg, secret), DECLINED); APR_IMPLEMENT_EXTERNAL_HOOK_RUN_FIRST(rt, REDWAX, int, process_pkcs11_out, (redwax_tool_t * r, const char *arg, apr_hash_t *secrets), (r, arg, secrets), DECLINED); APR_IMPLEMENT_EXTERNAL_HOOK_RUN_FIRST(rt, REDWAX, int, complete_pkcs11_out, (redwax_tool_t * r, const char *url, apr_hash_t *urls), (r, url, urls), DECLINED); APR_IMPLEMENT_EXTERNAL_HOOK_RUN_FIRST(rt, REDWAX, int, process_pkcs11_module_out, (redwax_tool_t * r, const char *arg), (r, arg), DECLINED); APR_IMPLEMENT_EXTERNAL_HOOK_RUN_FIRST(rt, REDWAX, int, complete_pkcs11_module_out, (redwax_tool_t * r, const char *mod, redwax_token_quoted_e quoted), (r, mod, quoted), DECLINED); APR_IMPLEMENT_EXTERNAL_HOOK_RUN_FIRST(rt, REDWAX, int, process_metadata_out, (redwax_tool_t * r, const char *arg), (r, arg), DECLINED); APR_IMPLEMENT_EXTERNAL_HOOK_RUN_FIRST(rt, REDWAX, int, process_calendar_out, (redwax_tool_t * r, const char *arg), (r, arg), DECLINED); APR_IMPLEMENT_EXTERNAL_HOOK_RUN_FIRST(rt, REDWAX, int, process_reminder_out, (redwax_tool_t * r, const char *arg), (r, arg), DECLINED); APR_IMPLEMENT_EXTERNAL_HOOK_RUN_FIRST(rt, REDWAX, int, process_ssh_public_out, (redwax_tool_t * r, const char *arg, const char *secret), (r, arg, secret), DECLINED); APR_IMPLEMENT_EXTERNAL_HOOK_RUN_FIRST(rt, REDWAX, int, set_format_out, (redwax_tool_t * r, const char *arg), (r, arg), DECLINED); APR_IMPLEMENT_EXTERNAL_HOOK_RUN_FIRST(rt, REDWAX, int, complete_format_out, (redwax_tool_t * r, apr_hash_t *formats), (r, formats), DECLINED); APR_IMPLEMENT_EXTERNAL_HOOK_RUN_FIRST(rt, REDWAX, int, set_order_out, (redwax_tool_t * r, const char *arg), (r, arg), DECLINED); APR_IMPLEMENT_EXTERNAL_HOOK_RUN_FIRST(rt, REDWAX, int, complete_order_out, (redwax_tool_t * r, apr_hash_t *orders), (r, orders), DECLINED); APR_IMPLEMENT_EXTERNAL_HOOK_RUN_FIRST(rt, REDWAX, int, set_calendar_alarm, (redwax_tool_t * r, const char *arg), (r, arg), DECLINED); APR_IMPLEMENT_EXTERNAL_HOOK_RUN_FIRST(rt, REDWAX, int, process_jwks_out, (redwax_tool_t * r, const char *arg), (r, arg), DECLINED); APR_IMPLEMENT_EXTERNAL_HOOK_RUN_FIRST(rt, REDWAX, apr_status_t, search_chain, (redwax_tool_t * r, const redwax_certificate_t *cert, const redwax_certificate_t **current), (r, cert, current), DECLINED); APR_IMPLEMENT_EXTERNAL_HOOK_RUN_FIRST(rt, REDWAX, apr_status_t, search_key, (redwax_tool_t * r, const redwax_certificate_t *cert), (r, cert), DECLINED); APR_IMPLEMENT_EXTERNAL_HOOK_RUN_FIRST(rt, REDWAX, apr_status_t, compare_certificate, (redwax_tool_t * r, const redwax_certificate_t *c1, const redwax_certificate_t *c2), (r, c1, c2), DECLINED); APR_IMPLEMENT_EXTERNAL_HOOK_RUN_FIRST(rt, REDWAX, apr_status_t, normalise_key, (redwax_tool_t * r, redwax_key_t *key, int index), (r, key, index), DECLINED); APR_IMPLEMENT_EXTERNAL_HOOK_RUN_FIRST(rt, REDWAX, apr_status_t, normalise_certificate, (redwax_tool_t * r, redwax_certificate_t *cert, int index), (r, cert, index), DECLINED); APR_IMPLEMENT_EXTERNAL_HOOK_RUN_ALL(rt, REDWAX, apr_status_t, add_dns_metadata, (redwax_tool_t *r, redwax_metadata_t *m, const redwax_certificate_t *cert), (r, m, cert), OK, DECLINED); #define REDWAX_TOOL_COMPLINE "COMP_LINE" #define REDWAX_TOOL_COMMANDLINE "COMMAND_LINE" #define REDWAX_TOOL_COMPPOINT "COMP_POINT" #define REDWAX_TOOL_COMP_WORDBREAKS "COMP_WORDBREAKS" #define REDWAX_TOOL_COMP_WORDBREAKS_DEFAULT "\"'><=;|&(:" #define REDWAX_TOOL_PEM_IN 256 #define REDWAX_TOOL_TRUST_PEM_IN 257 #define REDWAX_TOOL_PKCS11_IN 258 #define REDWAX_TOOL_PKCS11_MODULE_IN 259 #define REDWAX_TOOL_PKCS12_IN 260 #define REDWAX_TOOL_KEYCHAIN_IN 261 #define REDWAX_TOOL_FILTER 262 #define REDWAX_TOOL_FILTER_EMAIL 263 #define REDWAX_TOOL_FILTER_HOSTNAME 264 #define REDWAX_TOOL_FILTER_IP 265 #define REDWAX_TOOL_FILTER_CURRENT 266 #define REDWAX_TOOL_FILTER_DATE 267 #define REDWAX_TOOL_FILTER_EXPIRY 268 #define REDWAX_TOOL_CERT_OUT 269 #define REDWAX_TOOL_NO_CERT_OUT 270 #define REDWAX_TOOL_CHAIN_OUT 271 #define REDWAX_TOOL_NO_CHAIN_OUT 272 #define REDWAX_TOOL_ROOT_OUT 273 #define REDWAX_TOOL_NO_ROOT_OUT 274 #define REDWAX_TOOL_TRUST_OUT 275 #define REDWAX_TOOL_NO_TRUST_OUT 276 #define REDWAX_TOOL_CRL_OUT 277 #define REDWAX_TOOL_NO_CRL_OUT 278 #define REDWAX_TOOL_PARAM_OUT 279 #define REDWAX_TOOL_NO_PARAM_OUT 280 #define REDWAX_TOOL_KEY_IN 281 #define REDWAX_TOOL_NO_KEY_IN 282 #define REDWAX_TOOL_KEY_OUT 283 #define REDWAX_TOOL_NO_KEY_OUT 284 #define REDWAX_TOOL_AUTO_OUT 285 #define REDWAX_TOOL_NO_AUTO_OUT 286 #define REDWAX_TOOL_FILTER_VERIFY_PARAM 287 #define REDWAX_TOOL_SECRET_SUFFIX_IN 288 #define REDWAX_TOOL_SECRET_SUFFIX_OUT 289 #define REDWAX_TOOL_SECRET_TOKEN_IN 290 #define REDWAX_TOOL_SECRET_TOKEN_OUT 291 #define REDWAX_TOOL_LABEL_OUT 292 #define REDWAX_TOOL_NSS_OUT 293 #define REDWAX_TOOL_NSS_SLOT_OUT 294 #define REDWAX_TOOL_DER_OUT 295 #define REDWAX_TOOL_PEM_OUT 296 #define REDWAX_TOOL_PKCS12_OUT 297 #define REDWAX_TOOL_PKCS11_OUT 298 #define REDWAX_TOOL_PKCS11_MODULE_OUT 299 #define REDWAX_TOOL_METADATA_OUT 300 #define REDWAX_TOOL_METADATA_THRESHOLD 301 #define REDWAX_TOOL_FORMAT_OUT 302 #define REDWAX_TOOL_CALENDAR_OUT 303 #define REDWAX_TOOL_CALENDAR_ALARM 304 #define REDWAX_TOOL_REMINDER_OUT 305 #define REDWAX_TOOL_JWKS_OUT 306 #define REDWAX_TOOL_TEXT_OUT 307 #define REDWAX_TOOL_NO_TEXT_OUT 308 #define REDWAX_TOOL_SSH_PRIVATE_OUT 309 #define REDWAX_TOOL_SSH_PUBLIC_OUT 310 #define REDWAX_TOOL_SMIMEA_OUT 311 #define REDWAX_TOOL_SSHFP_OUT 312 #define REDWAX_TOOL_TLSA_OUT 313 #define REDWAX_TOOL_USER_IN 314 #define REDWAX_TOOL_USER_OUT 315 #define REDWAX_TOOL_GROUP_IN 316 #define REDWAX_TOOL_GROUP_OUT 317 #define REDWAX_TOOL_ORDER_OUT 318 #define REDWAX_EXIT_OK 0 #define REDWAX_EXIT_INIT 1 #define REDWAX_EXIT_OPTIONS 2 #define REDWAX_EXIT_FILTER 3 #define REDWAX_EXIT_AUTH 4 #define REDWAX_PASSTHROUGH "passthrough" #define REDWAX_SECRET_MAX HUGE_STRING_LEN static uid_t euid; static gid_t egid; static const apr_getopt_option_t cmdline_opts[] = { /* commands */ { "help", 'h', 0, " -h, --help\t\t\tDisplay this help message." }, { "version", 'v', 0, " -v, --version\t\t\tDisplay the version number." }, { "quiet", 'q', 0, " -q, --quiet\t\t\tBe quiet. Errors are suppressed." }, { "debug", 'd', 0, " -d, --debug\t\t\tBe loud. Print additional details of our progress." }, { "secret-suffix-in", REDWAX_TOOL_SECRET_SUFFIX_IN, 1, " --secret-suffix-in=suffix\tIf specified, secrets will be read from a file\n\t\t\t\twith the same name as the source file, and\n\t\t\t\tthe suffix specified. With value 'secret',\n\t\t\t\ta file 'key.pem' will have the secret loaded\n\t\t\t\tfrom 'key.secret' in the same directory." }, { "secret-suffix-out", REDWAX_TOOL_SECRET_SUFFIX_OUT, 1, " --secret-suffix-out=suffix\tIf specified, secrets will be read from a file\n\t\t\t\twith the same name as the target file, and\n\t\t\t\tthe suffix specified. With value 'secret',\n\t\t\t\ta file 'key.pem' will have the secret loaded\n\t\t\t\tfrom 'key.secret' in the same directory." }, { "secret-token-in", REDWAX_TOOL_SECRET_TOKEN_IN, 1, " --secret-token-in=file\tIf specified, secrets needed to read\n\t\t\t\tcertificates and keys from tokens will be read\n\t\t\t\tfrom a file one secret per line. Each secret\n\t\t\t\tis preceded by the name of the token and a\n\t\t\t\tcolon, as per the NSS pwdfile.txt file." }, { "secret-token-out", REDWAX_TOOL_SECRET_TOKEN_OUT, 1, " --secret-token-out=file\tIf specified, secrets needed to write\n\t\t\t\tcertificates and keys to tokens (PKCS11 and\n\t\t\t\tNSS) will be read from a file one secret per\n\t\t\t\tline. Each secret is preceded by the name of\n\t\t\t\tthe token and a colon, as per the NSS\n\t\t\t\tpwdfile.txt file." }, { "label-out", REDWAX_TOOL_LABEL_OUT, 1, " --label-out=label\t\tSet the name of the label to be applied to\n\t\t\t\tthe leaf certificates. If unspecified, the\n\t\t\t\tlabel is set to the subject of the certificate." }, { "pem-in", REDWAX_TOOL_PEM_IN, 1, " --pem-in=wildcard\t\tRead pem files from here. Use '-' for stdin." }, { "trust-pem-in", REDWAX_TOOL_TRUST_PEM_IN, 1, " --trust-pem-in=wildcard\tRead pem files containing trusted certificates from here. Use '-' for stdin." }, { "pkcs12-in", REDWAX_TOOL_PKCS12_IN, 1, " --pkcs12-in=file\t\tRead certificates, intermediate certificates,\n\t\t\t\troot certificates, crls, and keys from a PKCS12\n\t\t\t\tfile. Use '-' for stdin. Provide the secret\n\t\t\t\tusing --secret-suffix-in." }, { "pkcs11-in", REDWAX_TOOL_PKCS11_IN, 1, " --pkcs11-in=url\t\tRead certificates, intermediate certificates,\n\t\t\t\troot certificates, crls, and keys from a PKCS11\n\t\t\t\ttoken identified by the given url." }, { "pkcs11-module-in", REDWAX_TOOL_PKCS11_MODULE_IN, 1, " --pkcs11-module-in=mod\tSpecify the name of the PKCS11 module to be used,\n\t\t\t\toverriding system defaults. If relative, use the\n\t\t\t\tdefault PKCS11 module path, otherwise specify the\n\t\t\t\tabsolute path. Include the extension of the module." }, { "keychain-in", REDWAX_TOOL_KEYCHAIN_IN, 1, " --keychain-in=keychain\tRead certificates, intermediate certificates,\n\t\t\t\troot certificates, crls, and keys from a MacOS\n\t\t\t\tkeychain identified by the given name." }, { "filter", REDWAX_TOOL_FILTER, 1, " --filter=type\t\t\tApply the given filter to pass inputs to the\n\t\t\t\toutputs. \"search\" will pass through all\n\t\t\t\tcertificates matching the given hostname,\n\t\t\t\temail or ip address. \"verify\" will pass all\n\t\t\t\tleaf certificates that can be successfully\n\t\t\t\tverified through the certificate chain to a\n\t\t\t\ttrusted root certificate. With the default\n\t\t\t\t\"passthrough\", all certificates, csrs, and\n\t\t\t\tkeys are passed through." }, { "filter-email", REDWAX_TOOL_FILTER_EMAIL, 1, " --filter-email=address\tSearch/verify by the given email address. Leaf\n\t\t\t\tcertificates matching the email address will\n\t\t\t\tbe included. Can be specified more than once." }, { "filter-hostname", REDWAX_TOOL_FILTER_HOSTNAME, 1, " --filter-hostname=domain\tSearch/verify by the given hostname. Leaf\n\t\t\t\tcertificates matching the hostname will be\n\t\t\t\tkept, taking into account wildcards where\n\t\t\t\tpresent." }, { "filter-ip", REDWAX_TOOL_FILTER_IP, 1, " --filter-ip=address\t\tSearch/verify by the given IP address. Leaf\n\t\t\t\tcertificates matching the IP address will be\n\t\t\t\tincluded. Can be specified more than once." }, { "filter-current", REDWAX_TOOL_FILTER_CURRENT, 0, " --filter-current\t\tMatch the top ranking leaf certificate, and\n\t\t\t\tignore all other leaf certificates. The top\n\t\t\t\tcertificate is valid, and has the longest time\n\t\t\t\tto expiry." }, { "filter-verify-params", REDWAX_TOOL_FILTER_VERIFY_PARAM, 1, " --filter-verify-params=name\tSpecify the name of the set of parameters used\n\t\t\t\tfor verification. If unspecified, set to\n\t\t\t\t'default'." }, { "filter-date", REDWAX_TOOL_FILTER_DATE, 1, " --filter-date=date\t\tSet the date to be used for certificate\n\t\t\t\tverification. If unset, it will default to the\n\t\t\t\tcurrent time. Date format is generalized time\n\t\t\t\tsyntax as defined in RFC 4517 section 3.3.13." }, { "filter-expiry", REDWAX_TOOL_FILTER_EXPIRY, 1, " --filter-expiry=[option]\tVerify certificate expiry. 'check' does expiry\n\t\t\t\tverification. 'ignore' allows expired\n\t\t\t\tcertificates. 'ignore-leaf' allows expired leaf\n\t\t\t\tcertificates. 'ignore-chain' allows expired\n\t\t\t\tchain certificates. Default is 'check'." }, { "text-out", REDWAX_TOOL_TEXT_OUT, 0, " --text-out\t\t\tInclude additional text in certificate PEM and\n\t\t\t\tmetadata output." }, { "no-text-out", REDWAX_TOOL_NO_TEXT_OUT, 0, " --no-text-out\t\t\tExclude additional text in certificate PEM and\n\t\t\t\tmetadata output." }, { "cert-out", REDWAX_TOOL_CERT_OUT, 0, " --cert-out\t\t\tInclude leaf certificates in certificate output." }, { "no-cert-out", REDWAX_TOOL_NO_CERT_OUT, 0, " --no-cert-out\t\t\tExclude leaf certificates from certificate output." }, { "chain-out", REDWAX_TOOL_CHAIN_OUT, 0, " --chain-out\t\t\tSearch for and include intermediate\n\t\t\t\tcertificates belonging to leaf certificates in\n\t\t\t\tcertificate output. When verifying, if a chain\n\t\t\t\tcannot be created through intermediate\n\t\t\t\tcertificates to a trusted root certificate, the\n\t\t\t\tleaf certificate is ignored." }, { "no-chain-out", REDWAX_TOOL_NO_CHAIN_OUT, 0, " --no-chain-out\t\tExclude intermediate certificates belonging to\n\t\t\t\tleaf certificates in certificate output." }, { "root-out", REDWAX_TOOL_ROOT_OUT, 0, " --root-out\t\t\tSearch for and include root certificates at the\n\t\t\t\tend of the certificate chain. When verifying,\n\t\t\t\tif a chain cannot be created through intermediate\n\t\t\t\tcertificates to a root certificate, the leaf\n\t\t\t\tcertificate is ignored." }, { "no-root-out", REDWAX_TOOL_NO_ROOT_OUT, 0, " --no-root-out\t\t\tExclude root certificates at the end of the\n\t\t\t\tcertificate chain in output." }, { "trust-out", REDWAX_TOOL_TRUST_OUT, 0, " --trust-out\t\t\tIdentical to the 'root' option, however where\n\t\t\t\tsupported the output certificates will be\n\t\t\t\texported as a \"TRUSTED CERTIFICATE\" as\n\t\t\t\trecognised by OpenSSL." }, { "no-trust-out", REDWAX_TOOL_NO_TRUST_OUT, 0, " --no-trust-out\t\tExclude root certificates that would otherwise\n\t\t\t\tbe output as OpenSSL \"TRUSTED CERTIFICATE\"." }, { "crl-out", REDWAX_TOOL_CRL_OUT, 0, " --crl-out\t\t\tInclude certificate revocation lists in the output." }, { "no-crl-out", REDWAX_TOOL_NO_CRL_OUT, 0, " --no-crl-out\t\t\tExclude certificate revocation lists from the output." }, { "parameter-out", REDWAX_TOOL_PARAM_OUT, 0, " --parameter-out\t\tInclude key parameters in the output." }, { "no-parameter-out", REDWAX_TOOL_NO_PARAM_OUT, 0, " --no-parameter-out\t\tExclude key parameters from the output." }, { "key-in", REDWAX_TOOL_KEY_IN, 0, " --key-in\t\t\tRead private keys in the input. This will trigger a\n\t\t\t\tlogin attempt if needed." }, { "no-key-in", REDWAX_TOOL_NO_KEY_IN, 0, " --no-key-in\t\t\tExclude keys from the input." }, { "key-out", REDWAX_TOOL_KEY_OUT, 0, " --key-out\t\t\tInclude keys in the output." }, { "no-key-out", REDWAX_TOOL_NO_KEY_OUT, 0, " --no-key-out\t\t\tExclude keys from the output." }, { "auto-out", REDWAX_TOOL_AUTO_OUT, 0, " --auto-out\t\t\tOutput selectively. If a key or a certificate already\n\t\t\t\texists in a PKCS11 token, skip writing the key or\n\t\t\t\tcertificate. A key is considered to already exist if\n\t\t\t\tthe Subject Key Info of the incoming key matches the\n\t\t\t\tSubject Key Info field of an existing key on the\n\t\t\t\ttoken. A certificate is considered to already exist\n\t\t\t\tif another certificate with the same value is present\n\t\t\t\ton the token. When adding a certificate, look up the\n\t\t\t\tID of any corresponding key and use that ID for the\n\t\t\t\tcertificate (unless an ID is explicitly specified in\n\t\t\t\ta target URL)." }, { "no-auto-out", REDWAX_TOOL_NO_AUTO_OUT, 0, " --no-auto-out\t\t\tOutput everything as specified." }, { "nss-out", REDWAX_TOOL_NSS_OUT, 1, " --nss-out=directory\t\tWrite certificates, intermediate certificates,\n\t\t\t\troot certificates, crls, and keys to an NSS\n\t\t\t\tdatabase." }, { "nss-token-out", REDWAX_TOOL_NSS_SLOT_OUT, 1, " --nss-token-out=token\t\tSpecify the token to which certificates, intermediate\n\t\t\t\tcertificates, root certificates, crls, and keys will\n\t\t\t\tbe written to an NSS database. Must appear after the\n\t\t\t\t--nss-out option." }, { "der-out", REDWAX_TOOL_DER_OUT, 1, " --der-out=prefix\t\tWrite certificates, intermediate certificates,\n\t\t\t\troot certificates, crls, and keys. Each one is\n\t\t\t\twritten to a file with a suffix indicating type and\n\t\t\t\tindex. Use '-' for stdout, output will be concatenated." }, { "pem-out", REDWAX_TOOL_PEM_OUT, 1, " --pem-out=file\t\tWrite certificates, intermediate certificates,\n\t\t\t\troot certificates, crls, and keys. Use '-'\n\t\t\t\tfor stdout." }, { "pkcs12-out", REDWAX_TOOL_PKCS12_OUT, 1, " --pkcs12-out=file\t\tWrite certificates, intermediate certificates,\n\t\t\t\troot certificates, crls, and keys into a PKCS12\n\t\t\t\tfile. Use '-' for stdout." }, { "pkcs11-out", REDWAX_TOOL_PKCS11_OUT, 1, " --pkcs11-out=url\t\tWrite certificates, intermediate certificates,\n\t\t\t\troot certificates, crls, and keys into a PKCS11\n\t\t\t\ttoken identified by the given url." }, { "pkcs11-module-out", REDWAX_TOOL_PKCS11_MODULE_OUT, 1, " --pkcs11-module-out=mod\tSpecify the name of the PKCS11 module to be used,\n\t\t\t\toverriding system defaults. If relative, use the\n\t\t\t\tdefault PKCS11 module path, otherwise specify the\n\t\t\t\tabsolute path. Include the extension of the module." }, { "metadata-out", REDWAX_TOOL_METADATA_OUT, 1, " --metadata-out=file\t\tWrite metadata of each certificate and key to the\n\t\t\t\tgiven file in the format given by the format\n\t\t\t\tparameter." }, { "metadata-threshold", REDWAX_TOOL_METADATA_THRESHOLD, 1, " --metadata-threshold=days\tSet the threshold in days below which an expiry\n\t\t\t\tbecomes a warning. If unset, defaults to no\n\t\t\t\twarning." }, { "format-out", REDWAX_TOOL_FORMAT_OUT, 1, " --format-out=xml|json|yaml\tFormat of output metadata." }, { "calendar-out", REDWAX_TOOL_CALENDAR_OUT, 1, " --calendar-out=file\t\tWrite a calendar containing entries until the expiry\n\t\t\t\tdate of each certificate to the given file or\n\t\t\t\tdirectory. If a directory is specified, entries will\n\t\t\t\tbe created in discrete ICS files." }, { "reminder-out", REDWAX_TOOL_REMINDER_OUT, 1, " --reminder-out=file\t\tWrite a calendar containing reminders at the expiry\n\t\t\t\tdate of each certificate to the given file or\n\t\t\t\tdirectory. If a directory is specified, entries will\n\t\t\t\tbe created in discrete ICS files." }, { "calendar-alarm", REDWAX_TOOL_CALENDAR_ALARM, 1, " --calendar-alarm=duration\tIf specified, add an alarm to each calendar entry if\n\t\t\t\tnot already present. The alarm format is a RFC5545\n\t\t\t\tDURATION as described in section 3.3.6. Example:\n\t\t\t\t-P1W is one week prior to expiry." }, { "user-in", REDWAX_TOOL_USER_IN, 1, " --user-in=user\t\tUse the privileges of this user when reading\n\t\t\t\tcertificates and keys." }, { "user-out", REDWAX_TOOL_USER_OUT, 1, " --user-out=user\t\tUse the privileges of this user when writing\n\t\t\t\tcertificates and keys." }, { "group-in", REDWAX_TOOL_GROUP_IN, 1, " --group-in=group\t\tUse the privileges of this group when reading\n\t\t\t\tcertificates and keys. If you have set a user\n\t\t\t\tbefore setting a group, you may no longer have\n\t\t\t\tpermission to set the group. It is recommended\n\t\t\t\tthat if user and group are set, the group is set\n\t\t\t\tfirst." }, { "group-out", REDWAX_TOOL_GROUP_OUT, 1, " --group-out=group\t\t\tUse the privileges of this group when writing\n\t\t\t\t\tcertificates and keys. If you have set a user\n\t\t\t\t\tbefore setting a group, you may no longer have\n\t\t\t\t\tpermission to set the group. It is recommended\n\t\t\t\t\tthat if user and group are set, the group is set\n\t\t\t\t\tfirst." }, { "order-out", REDWAX_TOOL_ORDER_OUT, 1, " --order-out=[all|key-first|key-last]\tControls the order of keys and certificates in\n\t\t\t\t\tthe output. 'all' outputs all leaf certificates,\n\t\t\t\t\tfollowed by all intermediate certificates,\n\t\t\t\t\tfollowed by all root certificates, followed by\n\t\t\t\t\tall keys. 'key-first' outputs all certificates\n\t\t\t\t\twith a matching private key, with the private\n\t\t\t\t\tkey first, followed by the certificate, followed\n\t\t\t\t\tby intermediates and roots, followed by the\n\t\t\t\t\tprivate key of the next certificate and so on.\n\t\t\t\t\t'key-last' outputs all certificates with a\n\t\t\t\t\tmatching private key, with the certificate first,\n\t\t\t\t\tfollowed by intermediates and roots, followed by\n\t\t\t\t\tthe key of the certificate, finally followed by\n\t\t\t\t\tthe next certificate with a private key and so on." }, { "ssh-public-out", REDWAX_TOOL_SSH_PUBLIC_OUT, 1, " --ssh-public-out=file\t\tWrite an SSH public key to the given file." }, #if 0 { "jwks-out", REDWAX_TOOL_JWKS_OUT, 1, " --jwks-out=file\t\tWrite keys to the given file as an RFC7517 JSON\n\t\t\t\tWeb Key Set." }, { "ssh-private-out", REDWAX_TOOL_SSH_PRIVATE_OUT, 1, " --ssh-private-out=file\t\tWrite an SSH private key to the given file." }, { "smimea-out", REDWAX_TOOL_SMIMEA_OUT, 1, " --smimea-out=file\t\tWrite an SMIMEA DNS record to the given file." }, { "sshfp-out", REDWAX_TOOL_SSHFP_OUT, 1, " --sshfp-out=file\t\tWrite an SSHFP DNS record to the given file." }, { "tlsa-out", REDWAX_TOOL_TLSA_OUT, 1, " --tlsa-out=file\t\tWrite a TLSA DNS record to the given file." }, #endif { NULL } }; static int help(apr_file_t *out, const char *name, const char *msg, int code, const apr_getopt_option_t opts[]) { const char *n; int i = 0; n = strrchr(name, '/'); if (!n) { n = name; } else { n++; } apr_file_printf(out, "%s\n" "\n" "NAME\n" " %s - Redwax tool.\n" "\n" "SYNOPSIS\n" " %s [-v] [-h] [in options ...] [filter options ...] [out options ...]\n" "\n" "DESCRIPTION\n" " The redwax tool allows certificates and keys in a range of formats to\n" " be read and converted into other formats as needed by common services.\n" "\n" " Options are read in order in three phases. All input options are read,\n" " then all filter options, and then all output options.\n" "\n" "OPTIONS\n", msg ? msg : "", n, n); while (opts[i].name) { apr_file_printf(out, "%s\n\n", opts[i].description); i++; } apr_file_printf(out, "RETURN VALUE\n" " The redwax tool returns the following values.\n" "\n" " - 0: We completed our task successfully.\n" " - 1: We failed to initialise.\n" " - 2: The command line options were not valid.\n" " - 3: No certificates were passed through the filter.\n" " - 4: Could not become user or group.\n" "\n" "EXAMPLES\n" " In this example, we read all PEM files matching the wildcard, we pass\n" " all certificates through the filter, then we write chain certificates\n" " only to the file intermediates.pem in PEM format.\n" "\n" "\t~$ redwax-tool --pem-in *.pem --filter passthrough --chain-out \n" "\t\t--pem-out intermediates.pem\n" "\n" "AUTHOR\n" " Graham Leggett \n"); return code; } static int version(apr_file_t *out) { apr_file_printf(out, PACKAGE_STRING "\n"); return 0; } static int abortfunc(int retcode) { fprintf(stderr, "Out of memory.\n"); return retcode; } apr_status_t redwax_print_error(redwax_tool_t *r, const char *fmt, ...) { if (!r->quiet && !r->complete) { va_list ap; char *res; va_start(ap, fmt); res = apr_pvsprintf(r->pool, fmt, ap); va_end(ap); return apr_file_puts(res, r->err); } return APR_SUCCESS; } apr_status_t redwax_print_debug(redwax_tool_t *r, const char *fmt, ...) { if (r->debug && !r->quiet && !r->complete) { va_list ap; char *res; va_start(ap, fmt); res = apr_pvsprintf(r->pool, fmt, ap); va_end(ap); return apr_file_puts(res, r->err); } return APR_SUCCESS; } /* extended form of apr_tokenize_to_argv() */ apr_status_t redwax_tokenize_to_argv(const char *arg_str, const char ***argv_out, redwax_offset_t **argo_out, redwax_tokenize_state_t **state_out, redwax_tokenize_state_t *state, const char **err_out, apr_pool_t *pool) { char **argv; redwax_offset_t *argo = NULL; const char *cp; char *cc = NULL; const char *error = NULL; redwax_tokenize_state_t *states = NULL; redwax_tokenize_state_t st; unsigned int *offset = NULL; int numargs = 0, argnum; int length, equals; #define SKIP_WHITESPACE(cp) \ for ( ; apr_isspace(*cp); ) { \ cp++; \ }; /* HEX_TO_NIBBLE: * Convert a character representing a hex encoded 0-9, A-F or a-f and * convert it to a 4 bit nibble. */ #define HEX_TO_NIBBLE(cp,error) \ (*cp >= '0' && *cp <= '9') ? *cp - '0' : \ (*cp >= 'A' && *cp <= 'F') ? *cp - 'A' + 10 : \ (*cp >= 'a' && *cp <= 'f') ? *cp - 'a' + 10 : \ (!(error = cp)) /* last line of macro... */ /* * OCTAL_TO_3BITS: * Convert a character 0 through 7 as an octal character, and convert * it to 3 bits. */ #define OCTAL_TO_3BITS(cp,error) \ (*cp >= '0' && *cp <= '7') ? *cp - '0' : \ (!(error = cp)) /* last line of macro... */ if (state_out) { *state_out = states = apr_pcalloc(pool, (strlen(arg_str) + 1) * sizeof(redwax_tokenize_state_t)); } memcpy(&st, state, sizeof(*state)); cp = arg_str; SKIP_WHITESPACE(cp); /* This is ugly and expensive, but if anyone wants to figure a * way to support any number of args without counting and * allocating, please go ahead and change the code. * * Must account for the trailing NULL arg. */ /* first question - how many tokens? */ numargs = 1; while (*cp != '\0' && !error) { /* DETERMINE_NEXTTOKEN: * At exit, cp will point to one of the following: NULL, SPACE, TAB, NEWLINE, * CARRIAGE_RETURN. * NULL implies the argument string has been fully traversed. * * If error is not NULL, error will point at the character that generated the * error. */ #define DETERMINE_NEXTTOKEN(arg_str,cp,cc,offset,state,convert,length,error,equals) \ {int skip = 0; \ length = 0; \ equals = -1; \ error = NULL; \ state->intoken = REDWAX_TOKEN_INSIDE; \ state->equals = REDWAX_TOKEN_NOTSEEN; \ for ( ; *cp != '\0'; cp++) { \ char ch; \ ch = *cp; \ switch (state->escaped) { \ case REDWAX_TOKEN_NOESCAPE: /* no/was escape mode */ \ case REDWAX_TOKEN_WASESCAPE: \ state->escaped = REDWAX_TOKEN_NOESCAPE; \ switch (state->isquoted) { \ case REDWAX_TOKEN_NOQUOTE: /* no/was quote */ \ case REDWAX_TOKEN_WASQUOTE: \ state->isquoted = REDWAX_TOKEN_NOQUOTE; \ switch (ch) { \ case '"': \ state->isquoted = REDWAX_TOKEN_DOUBLEQUOTE; \ break; \ case '\'': \ state->isquoted = REDWAX_TOKEN_SINGLEQUOTE; \ break; \ case '\\': \ state->escaped = REDWAX_TOKEN_ESCAPE_SLASH; /* handle ansi c */ \ break; \ case ' ': \ case '\t': \ case '\n': \ case '\f': \ case '\r': \ state->intoken = REDWAX_TOKEN_OUTSIDE; \ skip = 1; /* end of token found, time to leave */ \ break; \ case '=': \ if (state->equals == REDWAX_TOKEN_NOTSEEN) { \ state->equals = REDWAX_TOKEN_SEEN; \ equals = length; \ } \ /* no break */ \ default: \ if (convert) { \ *cc++ = ch; \ if (offset) *offset++ = (cp - arg_str); \ } \ length++; \ break; \ }; \ break; \ case REDWAX_TOKEN_DOUBLEQUOTE: /* double quote */ \ switch (ch) { \ case '"': \ state->isquoted = REDWAX_TOKEN_WASQUOTE; \ break; \ case '\\': \ state->escaped = REDWAX_TOKEN_ESCAPE_SLASH; /* handle ansi c */ \ break; \ default: \ if (convert) { \ *cc++ = ch; \ if (offset) *offset++ = (cp - arg_str); \ } \ length++; \ break; \ }; \ break; \ case REDWAX_TOKEN_SINGLEQUOTE: /* single quote */ \ switch (ch) { \ case '\'': \ state->isquoted = REDWAX_TOKEN_WASQUOTE; \ break; \ default: \ if (convert) { \ *cc++ = ch; \ if (offset) *offset++ = (cp - arg_str); \ } \ length++; \ break; \ }; \ break; \ } \ break; \ case REDWAX_TOKEN_ESCAPE_SLASH: /* seen \ */ \ switch (ch) { \ case 'a': /* \a bell */ \ if (convert) { \ *cc++ = '\a'; \ if (offset) *offset++ = (cp - arg_str); \ } \ length++; \ state->escaped = REDWAX_TOKEN_WASESCAPE; \ break; \ case 'b': /* \b backspace */ \ if (convert) { \ *cc++ = '\b'; \ if (offset) *offset++ = (cp - arg_str); \ } \ length++; \ state->escaped = REDWAX_TOKEN_WASESCAPE; \ break; \ case 'c': /* \c control character */ \ state->escaped = REDWAX_TOKEN_ESCAPE_CONTROL; \ break; \ case 'e': /* \e escape */ \ if (convert) { \ *cc++ = '\e'; \ if (offset) *offset++ = (cp - arg_str); \ } \ length++; \ state->escaped = REDWAX_TOKEN_WASESCAPE; \ break; \ case 'f': /* \f form feed */ \ if (convert) { \ *cc++ = '\f'; \ if (offset) *offset++ = (cp - arg_str); \ } \ length++; \ state->escaped = REDWAX_TOKEN_WASESCAPE; \ break; \ case 'n': /* \n new line */ \ if (convert) { \ *cc++ = '\n'; \ if (offset) *offset++ = (cp - arg_str); \ } \ length++; \ state->escaped = REDWAX_TOKEN_WASESCAPE; \ break; \ case 'r': /* \n carriage return */ \ if (convert) { \ *cc++ = '\r'; \ if (offset) *offset++ = (cp - arg_str); \ } \ length++; \ state->escaped = REDWAX_TOKEN_WASESCAPE; \ break; \ case 't': /* \n horizontal tab */ \ if (convert) { \ *cc++ = '\t'; \ if (offset) *offset++ = (cp - arg_str); \ } \ length++; \ state->escaped = REDWAX_TOKEN_WASESCAPE; \ break; \ case 'v': /* \v vertical tab */ \ if (convert) { \ *cc++ = '\v'; \ if (offset) *offset++ = (cp - arg_str); \ } \ length++; \ state->escaped = REDWAX_TOKEN_WASESCAPE; \ break; \ case '0': \ case '1': \ case '2': \ case '3': /* \nnn octal number */ \ if (convert) { \ *cc = (OCTAL_TO_3BITS(cp,error)) << 6; /* no advance */ \ } \ else { \ OCTAL_TO_3BITS(cp,error); \ } \ state->escaped = REDWAX_TOKEN_ESCAPE_OCTAL2; \ break; \ case 'x': /* \x hex byte */ \ state->escaped = REDWAX_TOKEN_ESCAPE_HEX1; \ break; \ case 'u': /* \u 16 bit unicode */ \ state->escaped = REDWAX_TOKEN_ESCAPE_UTF16_1; \ break; \ case 'U': /* \U 32 bit unicode */ \ state->escaped = REDWAX_TOKEN_ESCAPE_UTF32_1; \ break; \ case '|': /* \| pipe */ \ case '&': /* \| ampersand */ \ case ';': /* \| semicolon */ \ case '<': /* \| less than */ \ case '>': /* \| greater than */ \ case '(': /* \| open bracket */ \ case ')': /* \| close bracket */ \ case '$': /* \| dollar */ \ case '`': /* \| backtick */ \ case '\\': /* \| backslash */ \ case '\"': /* \| double quote */ \ case '\'': /* \| single quote */ \ case ' ': /* space */ \ case '\t': /* tab */ \ case '\n': /* newline */ \ case '*': /* \| asterisk */ \ case '?': /* \| question mark */ \ case '[': /* \| open square bracket */ \ case '#': /* \| hash */ \ case '~': /* \| tilde */ \ case '=': /* \| equals */ \ case '%': /* \| percent */ \ if (convert) { \ *cc++ = ch; \ if (offset) *offset++ = (cp - arg_str); \ } \ length++; \ state->escaped = REDWAX_TOKEN_WASESCAPE; \ break; \ default: /* unknown character, error */ \ error = cp; \ break; \ }; \ break; \ case REDWAX_TOKEN_ESCAPE_OCTAL2: /* seen \[0-3][0-7] (octal) */ \ if (convert) { \ *cc |= (OCTAL_TO_3BITS(cp,error)) << 3; /* no advance */ \ } \ else { \ OCTAL_TO_3BITS(cp,error); \ } \ state->escaped = REDWAX_TOKEN_ESCAPE_OCTAL3; \ break; \ case REDWAX_TOKEN_ESCAPE_OCTAL3: /* seen \[0-3][0-7][0-7] (octal) */ \ if (convert) { \ *cc++ |= (OCTAL_TO_3BITS(cp,error)); /* advance */ \ if (offset) *offset++ = (cp - arg_str); \ } \ else { \ OCTAL_TO_3BITS(cp,error); \ } \ length++; \ state->escaped = REDWAX_TOKEN_WASESCAPE; \ break; \ case REDWAX_TOKEN_ESCAPE_HEX1: /* seen \x[H] (hex) */ \ if (convert) { \ *cc = (HEX_TO_NIBBLE(cp,error)) << 4; /* no advance */ \ } \ else { \ HEX_TO_NIBBLE(cp,error); \ } \ state->escaped = REDWAX_TOKEN_ESCAPE_HEX2; \ break; \ case REDWAX_TOKEN_ESCAPE_HEX2: /* seen \x[HH] (hex) */ \ if (convert) { \ *cc++ |= (HEX_TO_NIBBLE(cp,error)); /* advance */ \ if (offset) *offset++ = (cp - arg_str); \ } \ else { \ HEX_TO_NIBBLE(cp,error); \ } \ length++; \ state->escaped = REDWAX_TOKEN_WASESCAPE; \ break; \ case REDWAX_TOKEN_ESCAPE_UTF16_1: /* seen \u[H] (16 bit unicode) */ \ if (convert) { \ *cc = (HEX_TO_NIBBLE(cp,error)) << 4; /* no advance */ \ } \ else { \ HEX_TO_NIBBLE(cp,error); \ } \ state->escaped = REDWAX_TOKEN_ESCAPE_UTF16_2; \ break; \ case REDWAX_TOKEN_ESCAPE_UTF16_2: /* seen \u[HH] (16 bit unicode) */ \ if (convert) { \ *cc++ |= (HEX_TO_NIBBLE(cp,error)); /* advance */ \ if (offset) *offset++ = (cp - arg_str); \ } \ else { \ HEX_TO_NIBBLE(cp,error); \ } \ length++; \ state->escaped = REDWAX_TOKEN_ESCAPE_UTF16_3; \ break; \ case REDWAX_TOKEN_ESCAPE_UTF16_3: /* seen \u[HHH] (16 bit unicode) */ \ if (convert) { \ *cc = (HEX_TO_NIBBLE(cp,error)) << 4; /* no advance */ \ } \ else { \ HEX_TO_NIBBLE(cp,error); \ } \ state->escaped = REDWAX_TOKEN_ESCAPE_UTF16_4; \ break; \ case REDWAX_TOKEN_ESCAPE_UTF16_4: /* seen \u[HHHH] (16 bit unicode) */ \ if (convert) { \ *cc++ |= (HEX_TO_NIBBLE(cp,error)); /* advance */ \ if (offset) *offset++ = (cp - arg_str); \ } \ else { \ HEX_TO_NIBBLE(cp,error); \ } \ length++; \ state->escaped = REDWAX_TOKEN_WASESCAPE; \ break; \ case REDWAX_TOKEN_ESCAPE_UTF32_1: /* seen \U[H] (32 bit unicode) */ \ if (convert) { \ *cc = (HEX_TO_NIBBLE(cp,error)) << 4; /* no advance */ \ } \ else { \ HEX_TO_NIBBLE(cp,error); \ } \ state->escaped = REDWAX_TOKEN_ESCAPE_UTF32_2; \ break; \ case REDWAX_TOKEN_ESCAPE_UTF32_2: /* seen \U[HH] (32 bit unicode) */ \ if (convert) { \ *cc++ |= (HEX_TO_NIBBLE(cp,error)); /* advance */ \ if (offset) *offset++ = (cp - arg_str); \ } \ else { \ HEX_TO_NIBBLE(cp,error); \ } \ length++; \ state->escaped = REDWAX_TOKEN_ESCAPE_UTF32_3; \ break; \ case REDWAX_TOKEN_ESCAPE_UTF32_3: /* seen \U[HHH] (32 bit unicode) */ \ if (convert) { \ *cc = (HEX_TO_NIBBLE(cp,error)) << 4; /* no advance */ \ } \ else { \ HEX_TO_NIBBLE(cp,error); \ } \ state->escaped = REDWAX_TOKEN_ESCAPE_UTF32_4; \ break; \ case REDWAX_TOKEN_ESCAPE_UTF32_4: /* seen \U[HHHH] (32 bit unicode) */ \ if (convert) { \ *cc++ |= (HEX_TO_NIBBLE(cp,error)); /* advance */ \ if (offset) *offset++ = (cp - arg_str); \ } \ else { \ HEX_TO_NIBBLE(cp,error); \ } \ length++; \ state->escaped = REDWAX_TOKEN_ESCAPE_UTF32_5; \ break; \ case REDWAX_TOKEN_ESCAPE_UTF32_5: /* seen \U[H] (32 bit unicode) */ \ if (convert) { \ *cc = (HEX_TO_NIBBLE(cp,error)) << 4; /* no advance */ \ } \ else { \ HEX_TO_NIBBLE(cp,error); \ } \ state->escaped = REDWAX_TOKEN_ESCAPE_UTF32_6; \ break; \ case REDWAX_TOKEN_ESCAPE_UTF32_6: /* seen \U[HH] (32 bit unicode) */ \ if (convert) { \ *cc++ |= (HEX_TO_NIBBLE(cp,error)); /* advance */ \ if (offset) *offset++ = (cp - arg_str); \ } \ else { \ HEX_TO_NIBBLE(cp,error); \ } \ length++; \ state->escaped = REDWAX_TOKEN_ESCAPE_UTF32_7; \ break; \ case REDWAX_TOKEN_ESCAPE_UTF32_7: /* seen \U[HHH] (32 bit unicode) */ \ if (convert) { \ *cc = (HEX_TO_NIBBLE(cp,error)) << 4; /* no advance */ \ } \ else { \ HEX_TO_NIBBLE(cp,error); \ } \ state->escaped = REDWAX_TOKEN_ESCAPE_UTF32_8; \ break; \ case REDWAX_TOKEN_ESCAPE_UTF32_8: /* seen \U[HHHH] (32 bit unicode) */ \ if (convert) { \ *cc++ |= (HEX_TO_NIBBLE(cp,error)); /* advance */ \ if (offset) *offset++ = (cp - arg_str); \ } \ else { \ HEX_TO_NIBBLE(cp,error); \ } \ length++; \ state->escaped = REDWAX_TOKEN_WASESCAPE; \ break; \ case REDWAX_TOKEN_ESCAPE_CONTROL: \ switch (ch) { \ case '@': /* null */ \ case 'A': /* start of heading */ \ case 'B': /* start of text */ \ case 'C': /* end of text */ \ case 'D': /* end of transmit */ \ case 'E': /* enquiry */ \ case 'F': /* ack */ \ case 'G': /* bell */ \ case 'H': /* backspace */ \ case 'I': /* horizontal tab */ \ case 'J': /* linefeed */ \ case 'K': /* vertical tab */ \ case 'L': /* form feed */ \ case 'M': /* carriage return */ \ case 'N': /* shift out */ \ case 'O': /* shift in */ \ case 'P': /* data line escape */ \ case 'Q': /* redwax control 1 */ \ case 'R': /* redwax control 2 */ \ case 'S': /* redwax control 3 */ \ case 'T': /* redwax control 4 */ \ case 'U': /* nack */ \ case 'V': /* sync idel */ \ case 'W': /* end of transmit block */ \ case 'X': /* cancel */ \ case 'Y': /* end of medium */ \ case 'Z': /* substitute */ \ case '[': /* escape */ \ case '\\': /* file separator */ \ case ']': /* group separator */ \ case '^': /* record separator */ \ case '_': /* unit separator */ \ if (convert) { \ switch (ch) { \ case '@': /* null */ \ *cc++ = '\x00'; \ break; \ case 'A': /* start of heading */ \ *cc++ = '\x01'; \ break; \ case 'B': /* start of text */ \ *cc++ = '\x02'; \ break; \ case 'C': /* end of text */ \ *cc++ = '\x03'; \ break; \ case 'D': /* end of transmit */ \ *cc++ = '\x04'; \ break; \ case 'E': /* enquiry */ \ *cc++ = '\x05'; \ break; \ case 'F': /* ack */ \ *cc++ = '\x06'; \ break; \ case 'G': /* bell */ \ *cc++ = '\x07'; \ break; \ case 'H': /* backspace */ \ *cc++ = '\x08'; \ break; \ case 'I': /* horizontal tab */ \ *cc++ = '\x09'; \ break; \ case 'J': /* linefeed */ \ *cc++ = '\x0A'; \ break; \ case 'K': /* vertical tab */ \ *cc++ = '\x0B'; \ break; \ case 'L': /* form feed */ \ *cc++ = '\x0C'; \ break; \ case 'M': /* carriage return */ \ *cc++ = '\x0D'; \ break; \ case 'N': /* shift out */ \ *cc++ = '\x0E'; \ break; \ case 'O': /* shift in */ \ *cc++ = '\x0F'; \ break; \ case 'P': /* data line escape */ \ *cc++ = '\x10'; \ break; \ case 'Q': /* redwax control 1 */ \ *cc++ = '\x11'; \ break; \ case 'R': /* redwax control 2 */ \ *cc++ = '\x12'; \ break; \ case 'S': /* redwax control 3 */ \ *cc++ = '\x13'; \ break; \ case 'T': /* redwax control 4 */ \ *cc++ = '\x24'; \ break; \ case 'U': /* nack */ \ *cc++ = '\x25'; \ break; \ case 'V': /* sync idel */ \ *cc++ = '\x26'; \ break; \ case 'W': /* end of transmit block */ \ *cc++ = '\x27'; \ break; \ case 'X': /* cancel */ \ *cc++ = '\x28'; \ break; \ case 'Y': /* end of medium */ \ *cc++ = '\x29'; \ break; \ case 'Z': /* substitute */ \ *cc++ = '\x2A'; \ break; \ case '[': /* escape */ \ *cc++ = '\x2B'; \ break; \ case '\\': /* file separator */ \ *cc++ = '\x2C'; \ break; \ case ']': /* group separator */ \ *cc++ = '\x2D'; \ break; \ case '^': /* record separator */ \ *cc++ = '\x2E'; \ break; \ case '_': /* unit separator */ \ *cc++ = '\x2F'; \ break; \ } \ if (offset) *offset++ = (cp - arg_str); \ } \ length++; \ state->escaped = REDWAX_TOKEN_WASESCAPE; \ break; \ default: /* unknown character, error */ \ error = cp; \ break; \ }; \ break; \ default: /* unknown escape state, error */ \ error = cp; \ break; \ }; \ if (convert) { \ if (states) { \ memcpy(states++, state, sizeof(*state)); \ } \ } \ if (skip || error) { \ break; \ } \ } \ if (convert) { \ if (offset) *offset++ = (cp - arg_str); /* FIXME check this offset? */ \ }} /* last line of macro... */ DETERMINE_NEXTTOKEN(arg_str,cp,cc,offset,(&st),0,length,error,equals) if (error) { *err_out = error; return APR_EINVAL; } SKIP_WHITESPACE(cp); numargs++; } argv = apr_pcalloc(pool, numargs * sizeof(char*)); *argv_out = (const char **)argv; if (argo_out) { argo = apr_pcalloc(pool, numargs * sizeof(redwax_offset_t)); *argo_out = argo; } memcpy(&st, state, sizeof(*state)); // use ct instead cp = arg_str; SKIP_WHITESPACE(cp); /* second question - how long is each token? */ for (argnum = 0; argnum < (numargs-1); argnum++) { int start = cp - arg_str; DETERMINE_NEXTTOKEN(arg_str,cp,cc,offset,(&st),0,length,error,equals) argv[argnum] = apr_palloc(pool, length + 1); if (argo_out) { argo[argnum].offsets = apr_palloc(pool, (length + 1) * sizeof(unsigned int)); argo[argnum].size = length + 1; argo[argnum].start = start; argo[argnum].equals = equals; argo[argnum].end = cp - arg_str; } argv[argnum][length] = 0; SKIP_WHITESPACE(cp); } memcpy(&st, state, sizeof(*state)); cp = arg_str; SKIP_WHITESPACE(cp); /* let's munch on those tokens */ for (argnum = 0; argnum < (numargs-1); argnum++) { cc = argv[argnum]; if (argo_out) { offset = argo[argnum].offsets; } DETERMINE_NEXTTOKEN(arg_str,cp,cc,offset,(&st),1,length,error,equals) SKIP_WHITESPACE(cp); } memcpy(state, &st, sizeof(*state)); argv[argnum] = NULL; return APR_SUCCESS; } apr_status_t redwax_set_user(redwax_tool_t *r, const char *user) { struct passwd *pw; if (seteuid(euid)) { redwax_print_error(r, "Error: could not restore original user: %s\n", strerror(errno)); return apr_get_os_error(); } if (!user) { return APR_SUCCESS; } errno = 0; pw = getpwnam(user); if (pw) { if (seteuid(pw->pw_uid)) { redwax_print_error(r, "Error: could not set the user to '%s': %s\n", user, strerror(errno)); return apr_get_os_error(); } } else { redwax_print_error(r, "Error: user '%s' could not be found.\n", user); return APR_EGENERAL; } return APR_SUCCESS; } apr_status_t redwax_set_group(redwax_tool_t *r, const char *group) { struct group *gr; if (setegid(egid)) { redwax_print_error(r, "Error: could not restore original group: %s\n", strerror(errno)); return apr_get_os_error(); } if (!group) { return APR_SUCCESS; } gr = getgrnam(group); if (gr) { if (setegid(gr->gr_gid)) { redwax_print_error(r, "Error: could not set the group to '%s': %s\n", group, strerror(errno)); return apr_get_os_error(); } } else { redwax_print_error(r, "Error: group '%s' could not be found.\n", group); return APR_EGENERAL; } return APR_SUCCESS; } const char *redwax_home(redwax_tool_t *r, const char *path) { if (path[0] == '~') { if (r->home && r->home[0]) { path = apr_pstrcat(r->pool, r->home, path + 1, NULL); } } return path; } int redwax_is_secret_path(redwax_tool_t *r, const char *base, const char *secret_suffix) { const char *suffix = strrchr(base, '.'); if (suffix && secret_suffix && secret_suffix[0] && !strcmp(suffix + 1, secret_suffix)) { return 1; } return 0; } const char *redwax_secret_path(redwax_tool_t *r, const char *dir, const char *base, const char *secret_suffix) { const char *suffix = strrchr(base, '.'); if (secret_suffix && secret_suffix[0]) { char *path; apr_status_t status; if (suffix) { base = apr_psprintf(r->pool, "%.*s.%s", (int)(suffix - base), base, secret_suffix); } else { base = apr_psprintf(r->pool, "%s.%s", base, secret_suffix); } status = apr_filepath_merge(&path, dir, base, APR_FILEPATH_TRUENAME, r->pool); if (APR_SUCCESS == status) { return path; } } return NULL; } apr_hash_t *redwax_secrets_path(redwax_tool_t *r, const char *secrets_path) { if (secrets_path && secrets_path[0]) { apr_file_t *sfile; apr_status_t status; apr_size_t max = REDWAX_SECRET_MAX; apr_pool_t *pool; apr_hash_t *hash; apr_pool_create(&pool, r->pool); hash = apr_hash_make(pool); do { char *buf; char *lf; char *cl; if (!strcmp("-", secrets_path)) { sfile = r->in; } else { status = apr_file_open(&sfile, secrets_path, APR_FOPEN_READ, APR_FPROT_OS_DEFAULT, pool); if (APR_SUCCESS != status) { break; } } do { buf = apr_pcalloc(pool, max); #if HAVE_APR_CRYPTO_CLEAR apr_crypto_clear(pool, buf, max); #endif status = apr_file_gets(buf, max - 1, sfile); if (APR_EOF == status) { break; } else if (APR_SUCCESS != status) { redwax_print_error(r, "Could not read '%s': %pm\n", secrets_path, &status); apr_file_close(sfile); apr_pool_destroy(pool); return NULL; } /* string too long? */ if (strlen(buf) == max - 1) { redwax_print_error(r, "When reading '%s', line was too long (>%d)\n", secrets_path, (int) (max - 1)); apr_file_close(sfile); apr_pool_destroy(pool); return NULL; } /* ignore comments */ if (buf[0] == '#') { continue; } /* strip linefeed */ lf = strchr(buf, '\n'); if (lf) { *lf = 0; } /* isolate the token */ cl = strchr(buf, ':'); if (cl) { *cl = 0; apr_hash_set(hash, buf, cl - buf, cl + 1); } } while (1); } while (0); return hash; } return NULL; } apr_status_t redwax_complete_user(redwax_tool_t *r, const char *arg, redwax_token_quoted_e quoted) { int arglen = strlen(arg); setpwent(); while (1) { struct passwd *pw = getpwent(); if (!pw) { break; } if (!strncmp(arg, (const char *)pw->pw_name, arglen)) { apr_file_printf(r->out, "%s \n", redwax_pescape_echo_quoted(r->pool, (const char *)pw->pw_name, quoted, 1)); } } return APR_SUCCESS; } apr_status_t redwax_complete_group(redwax_tool_t *r, const char *arg, redwax_token_quoted_e quoted) { int arglen = strlen(arg); setgrent(); while (1) { struct group *gr = getgrent(); if (!gr) { break; } if (!strncmp(arg, (const char *)gr->gr_name, arglen)) { apr_file_printf(r->out, "%s \n", redwax_pescape_echo_quoted(r->pool, (const char *)gr->gr_name, quoted, 1)); } } return APR_SUCCESS; } apr_status_t redwax_complete_filter(redwax_tool_t *r, const char *arg, redwax_token_quoted_e quoted) { apr_hash_t *filters = apr_hash_make(r->pool); apr_hash_index_t *hi; void *val; int arglen = strlen(arg); rt_run_complete_filter(r, filters); for (hi = apr_hash_first(r->pool, filters); hi; hi = apr_hash_next(hi)) { apr_hash_this(hi, NULL, NULL, &val); if (!strncmp(arg, (const char *)val, arglen)) { apr_file_printf(r->out, "%s \n", redwax_pescape_echo_quoted(r->pool, (const char *)val, quoted, 1)); } } return APR_SUCCESS; } apr_status_t redwax_complete_directory(redwax_tool_t *r, const char *arg, redwax_token_quoted_e quoted) { apr_dir_t *thedir; apr_finfo_t dirent; const char *dir, *base, *prefix; apr_status_t status; int base_len; if (!arg[0]) { base = ""; dir = "."; prefix = ""; } else if (arg[strlen(arg) - 1] == '/') { base = ""; dir = apr_pstrdup(r->pool, arg); prefix = dir; } else { base = basename(apr_pstrdup(r->pool, arg)); dir = dirname(apr_pstrdup(r->pool, arg)); if (!strcmp(dir, "/")) { prefix = "/"; } else { prefix = apr_pstrcat(r->pool, dir, "/", NULL); } } base_len = strlen(base); dir = redwax_home(r, dir); if ((status = apr_dir_open(&thedir, dir, r->pool)) != APR_SUCCESS) { return status; } do { status = apr_dir_read(&dirent, APR_FINFO_TYPE | APR_FINFO_NAME | APR_FINFO_WPROT, thedir); if (APR_STATUS_IS_INCOMPLETE(status)) { continue; /* ignore un-stat()able files */ } else if (status != APR_SUCCESS) { break; } if (!strncmp(dirent.name, ".", 1)) { continue; } if (!strcmp(dirent.name, ".") || !strcmp(dirent.name, "..")) { continue; } if (redwax_is_secret_path(r, dirent.name, r->secret_suffix_in)) { continue; } if (redwax_is_secret_path(r, dirent.name, r->secret_suffix_out)) { continue; } switch (dirent.filetype) { case APR_DIR: { if (!strncmp(base, dirent.name, base_len)) { apr_file_printf(r->out, "%s/\n", redwax_pescape_echo_quoted(r->pool, apr_pstrcat(r->pool, prefix, dirent.name, NULL), quoted, 0)); } break; } default: continue; } } while (1); apr_dir_close(thedir); return APR_SUCCESS; } apr_status_t redwax_complete_file(redwax_tool_t *r, const char *arg, redwax_token_quoted_e quoted) { apr_dir_t *thedir; apr_finfo_t dirent; const char *dir, *base, *prefix; apr_status_t status; int base_len; if (!arg[0]) { base = ""; dir = "."; prefix = ""; apr_file_printf(r->out, "-\n"); } else if (arg[strlen(arg) - 1] == '/') { base = ""; dir = apr_pstrdup(r->pool, arg); prefix = dir; } else { base = basename(apr_pstrdup(r->pool, arg)); dir = dirname(apr_pstrdup(r->pool, arg)); if (!strcmp(dir, "/")) { prefix = "/"; } else { prefix = apr_pstrcat(r->pool, dir, "/", NULL); } } base_len = strlen(base); dir = redwax_home(r, dir); if ((status = apr_dir_open(&thedir, dir, r->pool)) != APR_SUCCESS) { return status; } do { status = apr_dir_read(&dirent, APR_FINFO_TYPE | APR_FINFO_NAME | APR_FINFO_WPROT, thedir); if (APR_STATUS_IS_INCOMPLETE(status)) { continue; /* ignore un-stat()able files */ } else if (status != APR_SUCCESS) { break; } if (!strncmp(dirent.name, ".", 1)) { continue; } if (!strcmp(dirent.name, ".") || !strcmp(dirent.name, "..")) { continue; } if (redwax_is_secret_path(r, dirent.name, r->secret_suffix_in)) { continue; } if (redwax_is_secret_path(r, dirent.name, r->secret_suffix_out)) { continue; } switch (dirent.filetype) { case APR_LNK: case APR_REG: { if (!strncmp(base, dirent.name, base_len)) { apr_file_printf(r->out, "%s \n", redwax_pescape_echo_quoted(r->pool, apr_pstrcat(r->pool, prefix, dirent.name, NULL), quoted, 1)); } break; } case APR_DIR: { if (!strncmp(base, dirent.name, base_len)) { apr_file_printf(r->out, "%s/\n", redwax_pescape_echo_quoted(r->pool, apr_pstrcat(r->pool, prefix, dirent.name, NULL), quoted, 0)); } break; } default: continue; } } while (1); apr_dir_close(thedir); return APR_SUCCESS; } apr_status_t redwax_complete_hostname(redwax_tool_t *r, const char *arg, redwax_token_quoted_e quoted) { apr_hash_index_t *hi; const void *key; apr_ssize_t klen; int arglen = strlen(arg); for (hi = apr_hash_first(r->pool, r->hostnames_index); hi; hi = apr_hash_next(hi)) { apr_hash_this(hi, &key, &klen, NULL); if (arglen <= klen && !strncmp(arg, (const char *)key, arglen)) { /* FIXME: wildcard matching, let's make it a thing */ apr_file_printf(r->out, "%s \n", redwax_pescape_echo_quoted(r->pool, apr_pstrndup(r->pool, (const char*) key, (int) klen), quoted, 1)); } } return APR_SUCCESS; } apr_status_t redwax_complete_email(redwax_tool_t *r, const char *arg, redwax_token_quoted_e quoted) { apr_hash_index_t *hi; const void *key; apr_ssize_t klen; int arglen = strlen(arg); for (hi = apr_hash_first(r->pool, r->emails_index); hi; hi = apr_hash_next(hi)) { apr_hash_this(hi, &key, &klen, NULL); if (arglen <= klen && !strncmp(arg, (const char *)key, arglen)) { apr_file_printf(r->out, "%s \n", redwax_pescape_echo_quoted(r->pool, apr_pstrndup(r->pool, (const char*) key, (int) klen), quoted, 1)); } } return APR_SUCCESS; } apr_status_t redwax_complete_ip(redwax_tool_t *r, const char *arg, redwax_token_quoted_e quoted) { apr_hash_index_t *hi; const void *key; apr_ssize_t klen; int arglen = strlen(arg); for (hi = apr_hash_first(r->pool, r->ips_index); hi; hi = apr_hash_next(hi)) { apr_hash_this(hi, &key, &klen, NULL); if (arglen <= klen && !strncmp(arg, (const char *)key, arglen)) { apr_file_printf(r->out, "%s \n", redwax_pescape_echo_quoted(r->pool, apr_pstrndup(r->pool, (const char*) key, (int) klen), quoted, 1)); } } return APR_SUCCESS; } static apr_status_t redwax_complete_verify_param(redwax_tool_t *r, const char *arg, redwax_token_quoted_e quoted) { apr_hash_t *params = apr_hash_make(r->pool); apr_hash_index_t *hi; void *val; int arglen = strlen(arg); rt_run_complete_verify_param(r, params); for (hi = apr_hash_first(r->pool, params); hi; hi = apr_hash_next(hi)) { apr_hash_this(hi, NULL, NULL, &val); if (!strncmp(arg, (const char *)val, arglen)) { apr_file_printf(r->out, "%s \n", redwax_pescape_echo_quoted(r->pool, (const char *)val, quoted, 1)); } } return APR_SUCCESS; } static apr_status_t redwax_complete_nss_token_out(redwax_tool_t *r, const char *arg, redwax_token_quoted_e quoted) { apr_hash_t *tokens = apr_hash_make(r->pool); apr_hash_index_t *hi; void *val; int arglen = strlen(arg); rt_run_complete_nss_token_out(r, tokens); for (hi = apr_hash_first(r->pool, tokens); hi; hi = apr_hash_next(hi)) { apr_hash_this(hi, NULL, NULL, &val); if (!strncmp(arg, (const char *)val, arglen)) { apr_file_printf(r->out, "%s \n", redwax_pescape_echo_quoted(r->pool, (const char *)val, quoted, 1)); } } return APR_SUCCESS; } static apr_status_t redwax_complete_pkcs11_in(redwax_tool_t *r, const char *arg, redwax_token_quoted_e quoted) { apr_hash_t *urls = apr_hash_make(r->pool); apr_hash_index_t *hi; void *val; apr_size_t skiplen = quoted == REDWAX_TOKEN_NOQUOTE ? redwax_strrcspn(arg, r->breaks) : 0; rt_run_complete_pkcs11_in(r, arg, urls); for (hi = apr_hash_first(r->pool, urls); hi; hi = apr_hash_next(hi)) { apr_hash_this(hi, NULL, NULL, &val); apr_file_printf(r->out, "%s\n", redwax_pescape_echo_quoted(r->pool, redwax_stroff((const char *)val, skiplen), quoted, 0)); } return APR_SUCCESS; } static apr_status_t redwax_complete_pkcs11_out(redwax_tool_t *r, const char *arg, redwax_token_quoted_e quoted) { apr_hash_t *urls = apr_hash_make(r->pool); apr_hash_index_t *hi; void *val; apr_size_t skiplen = quoted == REDWAX_TOKEN_NOQUOTE ? redwax_strrcspn(arg, r->breaks) : 0; rt_run_complete_pkcs11_out(r, arg, urls); for (hi = apr_hash_first(r->pool, urls); hi; hi = apr_hash_next(hi)) { apr_hash_this(hi, NULL, NULL, &val); apr_file_printf(r->out, "%s\n", redwax_pescape_echo_quoted(r->pool, redwax_stroff((const char *)val, skiplen), quoted, 0)); } return APR_SUCCESS; } static apr_status_t redwax_complete_keychain_in(redwax_tool_t *r, const char *arg, redwax_token_quoted_e quoted) { apr_hash_t *urls = apr_hash_make(r->pool); apr_hash_index_t *hi; void *val; apr_size_t skiplen = quoted == REDWAX_TOKEN_NOQUOTE ? redwax_strrcspn(arg, r->breaks) : 0; rt_run_complete_keychain_in(r, arg, urls); for (hi = apr_hash_first(r->pool, urls); hi; hi = apr_hash_next(hi)) { apr_hash_this(hi, NULL, NULL, &val); apr_file_printf(r->out, "%s\n", redwax_pescape_echo_quoted(r->pool, redwax_stroff((const char *)val, skiplen), quoted, 0)); } return APR_SUCCESS; } static apr_status_t redwax_complete_format_out(redwax_tool_t *r, const char *arg, redwax_token_quoted_e quoted) { apr_hash_t *formats = apr_hash_make(r->pool); apr_hash_index_t *hi; void *val; int arglen = strlen(arg); rt_run_complete_format_out(r, formats); for (hi = apr_hash_first(r->pool, formats); hi; hi = apr_hash_next(hi)) { apr_hash_this(hi, NULL, NULL, &val); if (!strncmp(arg, (const char *)val, arglen)) { apr_file_printf(r->out, "%s \n", redwax_pescape_echo_quoted(r->pool, (const char *)val, quoted, 1)); } } return APR_SUCCESS; } static apr_status_t redwax_complete_order_out(redwax_tool_t *r, const char *arg, redwax_token_quoted_e quoted) { apr_hash_t *orders = apr_hash_make(r->pool); apr_hash_index_t *hi; void *val; int arglen = strlen(arg); rt_run_complete_order_out(r, orders); for (hi = apr_hash_first(r->pool, orders); hi; hi = apr_hash_next(hi)) { apr_hash_this(hi, NULL, NULL, &val); if (!strncmp(arg, (const char *)val, arglen)) { apr_file_printf(r->out, "%s \n", redwax_pescape_echo_quoted(r->pool, (const char *)val, quoted, 1)); } } return APR_SUCCESS; } apr_status_t redwax_file_out(redwax_tool_t *r, const char *path, apr_status_t (out)(redwax_tool_t *r, const char *path, const char *secret)) { const char *dir, *base; apr_status_t status; if (!strcmp(path, "-")) { return out(r, path, NULL); } base = basename(apr_pstrdup(r->pool, path)); dir = dirname(apr_pstrdup(r->pool, path)); dir = redwax_home(r, dir); status = out(r, path, redwax_secret_path(r, dir, base, r->secret_suffix_out)); if (APR_SUCCESS != status) { return status; } return APR_SUCCESS; } apr_status_t redwax_token_out(redwax_tool_t *r, const char *path, const char *token, apr_status_t (out)(redwax_tool_t *r, const char *path, const char *token, apr_hash_t *secrets)) { apr_status_t status; if (!path) { return out(r, NULL, token, NULL); } if (!strcmp(path, "-")) { redwax_print_error(r, "Directory cannot be read from stdin.\n"); return APR_ENOENT; } status = out(r, path, token, redwax_secrets_path(r, r->secret_token_out)); if (APR_SUCCESS != status) { return status; } return APR_SUCCESS; } apr_status_t redwax_dir_walk(redwax_tool_t *r, const char *path, apr_status_t (walk)(redwax_tool_t *r, const char *path, const char *secret)) { apr_dir_t *thedir; apr_finfo_t dirent; const char *dir, *base; apr_status_t status; if (!strcmp(path, "-")) { return walk(r, path, NULL); } path = redwax_home(r, path); base = basename(apr_pstrdup(r->pool, path)); dir = dirname(apr_pstrdup(r->pool, path)); if (!apr_fnmatch_test(base)) { return walk(r, path, redwax_secret_path(r, dir, base, r->secret_suffix_in)); } if ((status = apr_dir_open(&thedir, dir, r->pool)) != APR_SUCCESS) { return status; } do { status = apr_dir_read(&dirent, APR_FINFO_TYPE | APR_FINFO_NAME | APR_FINFO_WPROT, thedir); if (APR_STATUS_IS_INCOMPLETE(status)) { continue; /* ignore un-stat()able files */ } else if (status != APR_SUCCESS) { break; } if (strcmp(dirent.name, ".") && strcmp(dirent.name, "..") && !redwax_is_secret_path(r, dirent.name, r->secret_suffix_in) && (apr_fnmatch(base, dirent.name, APR_FNM_PERIOD) == APR_SUCCESS)) { switch (dirent.filetype) { case APR_LNK: case APR_REG: { char *npath; status = apr_filepath_merge(&npath, dir, dirent.name, APR_FILEPATH_TRUENAME, r->pool); if (APR_SUCCESS != status) { apr_dir_close(thedir); return status; } status = walk(r, npath, redwax_secret_path(r, dir, dirent.name, r->secret_suffix_in)); if (APR_SUCCESS != status) { apr_dir_close(thedir); return status; } break; } case APR_DIR: { continue; } default: continue; } } } while (1); apr_dir_close(thedir); return APR_SUCCESS; } apr_status_t redwax_complete_filter_passthrough(redwax_tool_t *r, apr_hash_t *filters) { apr_hash_set(filters, REDWAX_PASSTHROUGH, strlen(REDWAX_PASSTHROUGH), REDWAX_PASSTHROUGH); return APR_SUCCESS; } apr_status_t redwax_process_filter_passthrough(redwax_tool_t *r, const char *arg) { if (strcmp(arg, REDWAX_PASSTHROUGH)) { return DECLINED; } r->certs_out = r->certs_in; r->intermediates_out = r->intermediates_in; r->trusted_out = r->trusted_in; r->keys_out = r->keys_in; return APR_SUCCESS; } static apr_status_t redwax_process_filter(redwax_tool_t *r, const char *arg) { apr_status_t status = rt_run_process_filter(r, arg); if (status) { r->rc = REDWAX_EXIT_FILTER; } r->filter.filter_applied = 1; return status; } static apr_status_t redwax_process_filter_default(redwax_tool_t *r, const char *arg) { redwax_print_error(r, "Filter '%s' is not supported.\n", arg); return APR_ENOENT; } static apr_status_t redwax_set_email(redwax_tool_t *r, const char *arg) { apr_hash_set(r->emails, arg, strlen(arg), arg); return APR_SUCCESS; } static apr_status_t redwax_set_hostname(redwax_tool_t *r, const char *arg) { apr_hash_set(r->hostnames, arg, strlen(arg), arg); return APR_SUCCESS; } static apr_status_t redwax_set_ip(redwax_tool_t *r, const char *arg) { apr_hash_set(r->ips, arg, strlen(arg), arg); return APR_SUCCESS; } static apr_status_t redwax_set_user_in(redwax_tool_t *r, const char *arg) { r->user_in = arg; redwax_set_user(r, arg); return APR_SUCCESS; } static apr_status_t redwax_set_user_out(redwax_tool_t *r, const char *arg) { r->user_out = arg; redwax_set_user(r, arg); return APR_SUCCESS; } static apr_status_t redwax_set_group_in(redwax_tool_t *r, const char *arg) { r->group_in = arg; redwax_set_group(r, arg); return APR_SUCCESS; } static apr_status_t redwax_set_group_out(redwax_tool_t *r, const char *arg) { r->group_out = arg; redwax_set_group(r, arg); return APR_SUCCESS; } static apr_status_t redwax_set_current(redwax_tool_t *r) { r->current = 1; return APR_SUCCESS; } static apr_status_t redwax_set_cert_out(redwax_tool_t *r) { r->cert_out = 1; return APR_SUCCESS; } static apr_status_t redwax_set_no_cert_out(redwax_tool_t *r) { r->cert_out = 0; return APR_SUCCESS; } static apr_status_t redwax_set_chain_out(redwax_tool_t *r) { r->chain_out = 1; return APR_SUCCESS; } static apr_status_t redwax_set_no_chain_out(redwax_tool_t *r) { r->chain_out = 0; return APR_SUCCESS; } static apr_status_t redwax_set_root_out(redwax_tool_t *r) { r->root_out = 1; return APR_SUCCESS; } static apr_status_t redwax_set_no_root_out(redwax_tool_t *r) { r->root_out = 0; return APR_SUCCESS; } static apr_status_t redwax_set_trust_out(redwax_tool_t *r) { r->trust_out = 1; return APR_SUCCESS; } static apr_status_t redwax_set_no_trust_out(redwax_tool_t *r) { r->trust_out = 0; return APR_SUCCESS; } static apr_status_t redwax_set_crl_out(redwax_tool_t *r) { r->crl_out = 1; return APR_SUCCESS; } static apr_status_t redwax_set_no_crl_out(redwax_tool_t *r) { r->crl_out = 0; return APR_SUCCESS; } static apr_status_t redwax_set_key_in(redwax_tool_t *r) { r->key_in = 1; return APR_SUCCESS; } static apr_status_t redwax_set_no_key_in(redwax_tool_t *r) { r->key_in = 0; return APR_SUCCESS; } static apr_status_t redwax_set_param_out(redwax_tool_t *r) { r->param_out = 1; return APR_SUCCESS; } static apr_status_t redwax_set_no_param_out(redwax_tool_t *r) { r->param_out = 0; return APR_SUCCESS; } static apr_status_t redwax_set_key_out(redwax_tool_t *r) { r->key_out = 1; return APR_SUCCESS; } static apr_status_t redwax_set_no_key_out(redwax_tool_t *r) { r->key_out = 0; return APR_SUCCESS; } static apr_status_t redwax_set_auto_out(redwax_tool_t *r) { r->auto_out = 1; return APR_SUCCESS; } static apr_status_t redwax_set_no_auto_out(redwax_tool_t *r) { r->auto_out = 0; return APR_SUCCESS; } static apr_status_t redwax_set_verify_param(redwax_tool_t *r, const char *arg) { apr_status_t status = rt_run_set_verify_param(r, arg); if (status) { r->rc = REDWAX_EXIT_OPTIONS; } return status; } static apr_status_t redwax_set_verify_date(redwax_tool_t *r, const char *arg) { apr_status_t status = rt_run_set_verify_date(r, arg); if (status) { r->rc = REDWAX_EXIT_OPTIONS; } return status; } static apr_status_t redwax_set_verify_expiry(redwax_tool_t *r, const char *arg) { apr_status_t status = rt_run_set_verify_expiry(r, arg); if (status) { r->rc = REDWAX_EXIT_OPTIONS; } return status; } static apr_status_t redwax_set_threshold(redwax_tool_t *r, const char *arg) { r->threshold = atoi(arg) * 86400; return APR_SUCCESS; } static apr_status_t redwax_set_secret_suffix_in(redwax_tool_t *r, const char *arg) { if (arg && arg[0]) { r->secret_suffix_in = arg; } else { r->secret_suffix_in = NULL; } return APR_SUCCESS; } static apr_status_t redwax_set_secret_suffix_out(redwax_tool_t *r, const char *arg) { if (arg && arg[0]) { r->secret_suffix_out = arg; } else { r->secret_suffix_out = NULL; } return APR_SUCCESS; } static apr_status_t redwax_set_secret_token_in(redwax_tool_t *r, const char *arg) { if (arg && arg[0]) { r->secret_token_in = redwax_home(r, arg); } else { r->secret_token_in = NULL; } return APR_SUCCESS; } static apr_status_t redwax_set_secret_token_out(redwax_tool_t *r, const char *arg) { if (arg && arg[0]) { r->secret_token_out = redwax_home(r, arg); } else { r->secret_token_out = NULL; } return APR_SUCCESS; } static apr_status_t redwax_set_label_out(redwax_tool_t *r, const char *arg) { if (arg && arg[0]) { r->label_out = arg; } else { r->label_out = NULL; } return APR_SUCCESS; } static apr_status_t redwax_nss_dir_out(redwax_tool_t *r, const char *arg) { if (!arg) { if (r->nss_out.needs_write) { redwax_token_out(r, r->nss_out.dir, NULL, &rt_run_process_nss_out); } r->nss_out.needs_write = 0; } else if (!r->nss_out.dir) { r->nss_out.dir = redwax_home(r, arg);; r->nss_out.needs_write = 1; } else { if (r->nss_out.needs_write) { redwax_token_out(r, r->nss_out.dir, NULL, &rt_run_process_nss_out); } r->nss_out.dir = redwax_home(r, arg);; r->nss_out.needs_write = 1; } return APR_SUCCESS; } static apr_status_t redwax_nss_token_out(redwax_tool_t *r, const char *arg) { redwax_token_out(r, r->nss_out.dir, arg, &rt_run_process_nss_out); r->nss_out.needs_write = 0; return APR_SUCCESS; } static apr_status_t redwax_pkcs11_in(redwax_tool_t *r, const char *arg) { apr_status_t status = rt_run_process_pkcs11_in(r, arg, redwax_secrets_path(r, r->secret_token_in)); apr_array_clear(r->pkcs11_in.pkcs11_modules); return status; } static apr_status_t redwax_pkcs11_module_in(redwax_tool_t *r, const char *arg) { APR_ARRAY_PUSH(r->pkcs11_in.pkcs11_modules, const char *) = arg; return APR_SUCCESS; } static apr_status_t redwax_pkcs11_out(redwax_tool_t *r, const char *arg) { apr_status_t status = rt_run_process_pkcs11_out(r, arg, redwax_secrets_path(r, r->secret_token_out)); apr_array_clear(r->pkcs11_out.pkcs11_modules); return status; } static apr_status_t redwax_pkcs11_module_out(redwax_tool_t *r, const char *arg) { APR_ARRAY_PUSH(r->pkcs11_out.pkcs11_modules, const char *) = arg; return APR_SUCCESS; } static apr_status_t redwax_metadata_out(redwax_tool_t *r, const char *arg) { arg = redwax_home(r, arg); apr_status_t status = rt_run_process_metadata_out(r, arg); return status; } static apr_status_t redwax_format_out(redwax_tool_t *r, const char *arg) { apr_status_t status = rt_run_set_format_out(r, arg); return status; } static apr_status_t redwax_order_out(redwax_tool_t *r, const char *arg) { apr_status_t status = rt_run_set_order_out(r, arg); return status; } static apr_status_t redwax_calendar_out(redwax_tool_t *r, const char *arg) { arg = redwax_home(r, arg); apr_status_t status = rt_run_process_calendar_out(r, arg); return status; } static apr_status_t redwax_calendar_alarm(redwax_tool_t *r, const char *arg) { apr_status_t status = rt_run_set_calendar_alarm(r, arg); return status; } static apr_status_t redwax_reminder_out(redwax_tool_t *r, const char *arg) { arg = redwax_home(r, arg); apr_status_t status = rt_run_process_reminder_out(r, arg); return status; } static apr_status_t redwax_jwks_out(redwax_tool_t *r, const char *arg) { arg = redwax_home(r, arg); apr_status_t status = rt_run_process_jwks_out(r, arg); return status; } static apr_status_t redwax_keychain_in(redwax_tool_t *r, const char *arg) { apr_status_t status = rt_run_process_keychain_in(r, arg); return status; } void redwax_add_default_hooks() { rt_hook_complete_filter(redwax_complete_filter_passthrough, NULL, NULL, APR_HOOK_MIDDLE); rt_hook_process_filter(redwax_process_filter_passthrough, NULL, NULL, APR_HOOK_MIDDLE); rt_hook_process_filter(redwax_process_filter_default, NULL, NULL, APR_HOOK_REALLY_LAST); } int redwax_tool_argv(redwax_tool_t *r, int argc, const char *const *argv) { apr_status_t status; apr_getopt_t *opt; const char *optarg; int optch; /* walk standalone options */ apr_getopt_init(&opt, r->pool, argc, argv); while ((status = apr_getopt_long(opt, cmdline_opts, &optch, &optarg)) == APR_SUCCESS) { switch (optch) { case 'v': { version(r->out); return REDWAX_EXIT_OK; } case 'h': { help(r->out, argv[0], NULL, 0, cmdline_opts); return REDWAX_EXIT_OK; } case 'q': { r->quiet++; break; } case 'd': { r->debug++; break; } } } if (APR_SUCCESS != status && APR_EOF != status) { return REDWAX_EXIT_OPTIONS; } if (opt->ind != opt->argc) { apr_file_printf(r->err, "Stray arguments present, bailing out. Do you have unquoted wildcards?\n"); return REDWAX_EXIT_OPTIONS; } /* walk input options */ apr_getopt_init(&opt, r->pool, argc, argv); while ((status = apr_getopt_long(opt, cmdline_opts, &optch, &optarg)) == APR_SUCCESS) { switch (optch) { case REDWAX_TOOL_USER_IN: { if (redwax_set_user_in(r, optarg)) { return REDWAX_EXIT_AUTH; } break; } case REDWAX_TOOL_GROUP_IN: { if (redwax_set_group_in(r, optarg)) { return REDWAX_EXIT_AUTH; } break; } case REDWAX_TOOL_KEY_IN: { redwax_set_key_in(r); break; } case REDWAX_TOOL_NO_KEY_IN: { redwax_set_no_key_in(r); break; } case REDWAX_TOOL_SECRET_SUFFIX_IN: { redwax_set_secret_suffix_in(r, optarg); break; } case REDWAX_TOOL_SECRET_TOKEN_IN: { redwax_set_secret_token_in(r, optarg); break; } case REDWAX_TOOL_PEM_IN: { redwax_dir_walk(r, optarg, &rt_run_process_pem_in); break; } case REDWAX_TOOL_TRUST_PEM_IN: { redwax_dir_walk(r, optarg, &rt_run_process_trust_pem_in); break; } case REDWAX_TOOL_PKCS11_IN: { redwax_pkcs11_in(r, optarg); break; } case REDWAX_TOOL_PKCS11_MODULE_IN: { redwax_pkcs11_module_in(r, optarg); break; } case REDWAX_TOOL_PKCS12_IN: { redwax_dir_walk(r, optarg, &rt_run_process_pkcs12_in); break; } case REDWAX_TOOL_KEYCHAIN_IN: { redwax_keychain_in(r, optarg); break; } } } redwax_set_user(r, NULL); redwax_set_group(r, NULL); /* walk filter options */ apr_getopt_init(&opt, r->pool, argc, argv); while ((status = apr_getopt_long(opt, cmdline_opts, &optch, &optarg)) == APR_SUCCESS) { switch (optch) { case REDWAX_TOOL_FILTER_EMAIL: { redwax_set_email(r, optarg); break; } case REDWAX_TOOL_FILTER_HOSTNAME: { redwax_set_hostname(r, optarg); break; } case REDWAX_TOOL_FILTER_IP: { redwax_set_ip(r, optarg); break; } case REDWAX_TOOL_FILTER_CURRENT: { redwax_set_current(r); break; } case REDWAX_TOOL_FILTER_VERIFY_PARAM: { if (redwax_set_verify_param(r, optarg)) { return REDWAX_EXIT_OPTIONS; } break; } case REDWAX_TOOL_FILTER_DATE: { if (redwax_set_verify_date(r, optarg)) { return REDWAX_EXIT_OPTIONS; } break; } case REDWAX_TOOL_FILTER_EXPIRY: { if (redwax_set_verify_expiry(r, optarg)) { return REDWAX_EXIT_OPTIONS; } break; } } } /* walk filters */ apr_getopt_init(&opt, r->pool, argc, argv); while ((status = apr_getopt_long(opt, cmdline_opts, &optch, &optarg)) == APR_SUCCESS) { switch (optch) { case REDWAX_TOOL_FILTER: { redwax_process_filter(r, optarg); break; } } } /* filter catchall */ if (!r->filter.filter_applied) { redwax_process_filter(r, REDWAX_PASSTHROUGH); } redwax_set_user(r, NULL); redwax_set_group(r, NULL); /* walk output options */ apr_getopt_init(&opt, r->pool, argc, argv); while ((status = apr_getopt_long(opt, cmdline_opts, &optch, &optarg)) == APR_SUCCESS) { switch (optch) { case REDWAX_TOOL_USER_OUT: { if (redwax_set_user_out(r, optarg)) { return REDWAX_EXIT_AUTH; } break; } case REDWAX_TOOL_GROUP_OUT: { if (redwax_set_group_out(r, optarg)) { return REDWAX_EXIT_AUTH; } break; } case REDWAX_TOOL_TEXT_OUT: { r->text++; break; } case REDWAX_TOOL_NO_TEXT_OUT: { r->text--; break; } case REDWAX_TOOL_CERT_OUT: { redwax_set_cert_out(r); break; } case REDWAX_TOOL_NO_CERT_OUT: { redwax_set_no_cert_out(r); break; } case REDWAX_TOOL_CHAIN_OUT: { redwax_set_chain_out(r); break; } case REDWAX_TOOL_NO_CHAIN_OUT: { redwax_set_no_chain_out(r); break; } case REDWAX_TOOL_ROOT_OUT: { redwax_set_root_out(r); break; } case REDWAX_TOOL_NO_ROOT_OUT: { redwax_set_no_root_out(r); break; } case REDWAX_TOOL_TRUST_OUT: { redwax_set_trust_out(r); break; } case REDWAX_TOOL_NO_TRUST_OUT: { redwax_set_no_trust_out(r); break; } case REDWAX_TOOL_CRL_OUT: { redwax_set_crl_out(r); break; } case REDWAX_TOOL_NO_CRL_OUT: { redwax_set_no_crl_out(r); break; } case REDWAX_TOOL_PARAM_OUT: { redwax_set_param_out(r); break; } case REDWAX_TOOL_NO_PARAM_OUT: { redwax_set_no_param_out(r); break; } case REDWAX_TOOL_KEY_OUT: { redwax_set_key_out(r); break; } case REDWAX_TOOL_NO_KEY_OUT: { redwax_set_no_key_out(r); break; } case REDWAX_TOOL_AUTO_OUT: { redwax_set_auto_out(r); break; } case REDWAX_TOOL_NO_AUTO_OUT: { redwax_set_no_auto_out(r); break; } case REDWAX_TOOL_LABEL_OUT: { redwax_set_label_out(r, optarg); break; } case REDWAX_TOOL_SECRET_SUFFIX_OUT: { redwax_set_secret_suffix_out(r, optarg); break; } case REDWAX_TOOL_SECRET_TOKEN_OUT: { redwax_set_secret_token_out(r, optarg); break; } case REDWAX_TOOL_NSS_OUT: { redwax_nss_dir_out(r, optarg); break; } case REDWAX_TOOL_NSS_SLOT_OUT: { redwax_nss_token_out(r, optarg); break; } case REDWAX_TOOL_DER_OUT: { redwax_file_out(r, optarg, &rt_run_process_der_out); break; } case REDWAX_TOOL_PEM_OUT: { redwax_file_out(r, optarg, &rt_run_process_pem_out); break; } case REDWAX_TOOL_PKCS12_OUT: { redwax_file_out(r, optarg, &rt_run_process_pkcs12_out); break; } case REDWAX_TOOL_PKCS11_OUT: { redwax_pkcs11_out(r, optarg); break; } case REDWAX_TOOL_PKCS11_MODULE_OUT: { redwax_pkcs11_module_out(r, optarg); break; } case REDWAX_TOOL_METADATA_OUT: { redwax_metadata_out(r, optarg); break; } case REDWAX_TOOL_METADATA_THRESHOLD: { if (redwax_set_threshold(r, optarg)) { return REDWAX_EXIT_OPTIONS; } break; } case REDWAX_TOOL_CALENDAR_OUT: { redwax_calendar_out(r, optarg); break; } case REDWAX_TOOL_CALENDAR_ALARM: { redwax_calendar_alarm(r, optarg); break; } case REDWAX_TOOL_REMINDER_OUT: { redwax_reminder_out(r, optarg); break; } case REDWAX_TOOL_SSH_PUBLIC_OUT: { redwax_file_out(r, optarg, &rt_run_process_ssh_public_out); break; } case REDWAX_TOOL_FORMAT_OUT: { redwax_format_out(r, optarg); break; } case REDWAX_TOOL_ORDER_OUT: { if (redwax_order_out(r, optarg)) { return REDWAX_EXIT_OPTIONS; } break; } case REDWAX_TOOL_JWKS_OUT: { redwax_jwks_out(r, optarg); break; } } } /* output catch-all */ redwax_nss_dir_out(r, NULL); redwax_set_user(r, NULL); redwax_set_group(r, NULL); return r->rc; } void redwax_tool_compgen_options(redwax_tool_t *r, const char *prefix) { int i = 0; int prefix_len = strlen(prefix); /* * Single dash - list all options short and long */ if (!strcmp(prefix, "-")) { while (cmdline_opts[i].name) { if (cmdline_opts[i].name) { apr_file_printf(r->out, "--%s \n", cmdline_opts[i].name); } else if (cmdline_opts[i].optch < 128) { apr_file_printf(r->out, "-%c \n", (char)cmdline_opts[i].optch); } i++; } } /* * Double dash - list options with a prefix match */ else if (!strncmp(prefix, "--", 2)) { prefix_len -= 2; prefix += 2; while (cmdline_opts[i].name) { if (!strncmp(prefix, cmdline_opts[i].name, prefix_len)) { apr_file_printf(r->out, "--%s \n", cmdline_opts[i].name); } i++; } } } int redwax_tool_compgen(redwax_tool_t *r, const char *line) { const char **argv; const char *optarg; apr_status_t status; apr_getopt_t *opt; const char *error; redwax_tokenize_state_t state = { 0 }; int optch; int argc; /* we need to be quiet when completing */ r->complete = 1; if (APR_SUCCESS != (status = redwax_tokenize_to_argv(line, &argv, NULL, NULL, &state, &error, r->tpool))) { printf("error: %s\n", error); /* do nothing */ return REDWAX_EXIT_OPTIONS; } else if (!argv) { /* do nothing */ return REDWAX_EXIT_OK; } for (argc = 0; argv[argc]; argc++); apr_getopt_init(&opt, r->pool, argc, (const char *const *)argv); opt->errfn = NULL; while ((status = apr_getopt_long(opt, cmdline_opts, &optch, &optarg)) == APR_SUCCESS) { /* * Are we processing the last unfinished option? */ if (opt->ind == opt->argc && state.intoken == REDWAX_TOKEN_INSIDE) { switch (optch) { case 'v': { /* no completion */ break; } case 'h': { /* no completion */ break; } case REDWAX_TOOL_PEM_IN: { redwax_complete_file(r, optarg, state.isquoted); break; } case REDWAX_TOOL_TRUST_PEM_IN: { redwax_complete_file(r, optarg, state.isquoted); break; } case REDWAX_TOOL_PKCS11_IN: { redwax_complete_pkcs11_in(r, optarg, state.isquoted); break; } case REDWAX_TOOL_PKCS11_MODULE_IN: { rt_run_complete_pkcs11_module_in(r, optarg, state.isquoted); break; } case REDWAX_TOOL_KEYCHAIN_IN: { redwax_complete_keychain_in(r, optarg, state.isquoted); break; } case REDWAX_TOOL_SECRET_TOKEN_IN: { redwax_complete_file(r, optarg, state.isquoted); break; } case REDWAX_TOOL_PKCS12_IN: { redwax_complete_file(r, optarg, state.isquoted); break; } case REDWAX_TOOL_USER_IN: { redwax_complete_user(r, optarg, state.isquoted); break; } case REDWAX_TOOL_GROUP_IN: { redwax_complete_group(r, optarg, state.isquoted); break; } case REDWAX_TOOL_FILTER: { redwax_complete_filter(r, optarg, state.isquoted); break; } case REDWAX_TOOL_FILTER_HOSTNAME: { redwax_complete_hostname(r, optarg, state.isquoted); break; } case REDWAX_TOOL_FILTER_EMAIL: { redwax_complete_email(r, optarg, state.isquoted); break; } case REDWAX_TOOL_FILTER_IP: { redwax_complete_ip(r, optarg, state.isquoted); break; } case REDWAX_TOOL_FILTER_VERIFY_PARAM: { redwax_complete_verify_param(r, optarg, state.isquoted); break; } case REDWAX_TOOL_NSS_OUT: { redwax_complete_directory(r, optarg, state.isquoted); break; } case REDWAX_TOOL_NSS_SLOT_OUT: { redwax_complete_nss_token_out(r, optarg, state.isquoted); break; } case REDWAX_TOOL_DER_OUT: { redwax_complete_file(r, optarg, state.isquoted); break; } case REDWAX_TOOL_PEM_OUT: { redwax_complete_file(r, optarg, state.isquoted); break; } case REDWAX_TOOL_PKCS12_OUT: { redwax_complete_file(r, optarg, state.isquoted); break; } case REDWAX_TOOL_PKCS11_OUT: { redwax_complete_pkcs11_out(r, optarg, state.isquoted); break; } case REDWAX_TOOL_PKCS11_MODULE_OUT: { rt_run_complete_pkcs11_module_out(r, optarg, state.isquoted); break; } case REDWAX_TOOL_SECRET_TOKEN_OUT: { redwax_complete_file(r, optarg, state.isquoted); break; } case REDWAX_TOOL_METADATA_OUT: { redwax_complete_file(r, optarg, state.isquoted); break; } case REDWAX_TOOL_FORMAT_OUT: { redwax_complete_format_out(r, optarg, state.isquoted); break; } case REDWAX_TOOL_JWKS_OUT: { redwax_complete_file(r, optarg, state.isquoted); break; } case REDWAX_TOOL_USER_OUT: { redwax_complete_user(r, optarg, state.isquoted); break; } case REDWAX_TOOL_GROUP_OUT: { redwax_complete_group(r, optarg, state.isquoted); break; } case REDWAX_TOOL_ORDER_OUT: { redwax_complete_order_out(r, optarg, state.isquoted); break; } } } /* * Parse previous options to be ready for completion. */ else { switch (optch) { case 'v': { /* no completion */ break; } case 'h': { /* no completion */ break; } case REDWAX_TOOL_SECRET_SUFFIX_IN: { redwax_set_secret_suffix_in(r, optarg); break; } case REDWAX_TOOL_SECRET_TOKEN_IN: { redwax_set_secret_token_in(r, optarg); break; } case REDWAX_TOOL_PEM_IN: { redwax_dir_walk(r, optarg, &rt_run_process_pem_in); break; } case REDWAX_TOOL_TRUST_PEM_IN: { redwax_dir_walk(r, optarg, &rt_run_process_trust_pem_in); break; } case REDWAX_TOOL_PKCS11_MODULE_IN: { redwax_pkcs11_module_in(r, optarg); break; } case REDWAX_TOOL_PKCS12_IN: { redwax_dir_walk(r, optarg, &rt_run_process_pkcs12_in); break; } case REDWAX_TOOL_NSS_OUT: { r->nss_out.dir = optarg; break; } case REDWAX_TOOL_PKCS11_MODULE_OUT: { redwax_pkcs11_module_out(r, optarg); break; } } } } /* * Are we processing the last unfinished option? */ if (opt->ind != opt->argc) { /* bail out early, no completion */ return REDWAX_EXIT_OK; } switch (status) { case APR_BADCH: { /* * The option is invalid or unrecognised, perform option * completion. */ if (state.intoken == REDWAX_TOKEN_INSIDE) { redwax_tool_compgen_options(r, opt->argv[opt->ind - 1]); } break; } case APR_BADARG: { /* * The option is missing an argument, perform completion based * on the value of the option, which will be in optch. */ switch (optch) { case 'v': { /* no completion */ break; } case 'h': { /* no completion */ break; } case REDWAX_TOOL_PEM_IN: { redwax_complete_file(r, "", state.isquoted); break; } case REDWAX_TOOL_TRUST_PEM_IN: { redwax_complete_file(r, "", state.isquoted); break; } case REDWAX_TOOL_PKCS11_IN: { redwax_complete_pkcs11_in(r, "", state.isquoted); break; } case REDWAX_TOOL_PKCS11_MODULE_IN: { rt_run_complete_pkcs11_module_in(r, "", state.isquoted); break; } case REDWAX_TOOL_PKCS12_IN: { redwax_complete_file(r, "", state.isquoted); break; } case REDWAX_TOOL_KEYCHAIN_IN: { redwax_complete_keychain_in(r, "", state.isquoted); break; } case REDWAX_TOOL_SECRET_TOKEN_IN: { redwax_complete_file(r, "", state.isquoted); break; } case REDWAX_TOOL_USER_IN: { redwax_complete_user(r, "", state.isquoted); break; } case REDWAX_TOOL_GROUP_IN: { redwax_complete_group(r, "", state.isquoted); break; } case REDWAX_TOOL_FILTER: { redwax_complete_filter(r, "", state.isquoted); break; } case REDWAX_TOOL_FILTER_EMAIL: { redwax_complete_email(r, "", state.isquoted); break; } case REDWAX_TOOL_FILTER_HOSTNAME: { redwax_complete_hostname(r, "", state.isquoted); break; } case REDWAX_TOOL_FILTER_IP: { redwax_complete_ip(r, "", state.isquoted); break; } case REDWAX_TOOL_FILTER_VERIFY_PARAM: { redwax_complete_verify_param(r, "", state.isquoted); break; } case REDWAX_TOOL_NSS_OUT: { redwax_complete_directory(r, "", state.isquoted); break; } case REDWAX_TOOL_NSS_SLOT_OUT: { redwax_complete_nss_token_out(r, "", state.isquoted); break; } case REDWAX_TOOL_DER_OUT: { redwax_complete_file(r, "", state.isquoted); break; } case REDWAX_TOOL_PEM_OUT: { redwax_complete_file(r, "", state.isquoted); break; } case REDWAX_TOOL_PKCS12_OUT: { redwax_complete_file(r, "", state.isquoted); break; } case REDWAX_TOOL_PKCS11_OUT: { redwax_complete_pkcs11_out(r, "", state.isquoted); break; } case REDWAX_TOOL_PKCS11_MODULE_OUT: { rt_run_complete_pkcs11_module_out(r, "", state.isquoted); break; } case REDWAX_TOOL_SECRET_TOKEN_OUT: { redwax_complete_file(r, "", state.isquoted); break; } case REDWAX_TOOL_METADATA_OUT: { redwax_complete_file(r, "", state.isquoted); break; } case REDWAX_TOOL_FORMAT_OUT: { redwax_complete_format_out(r, "", state.isquoted); break; } case REDWAX_TOOL_JWKS_OUT: { redwax_complete_file(r, "", state.isquoted); break; } case REDWAX_TOOL_USER_OUT: { redwax_complete_user(r, "", state.isquoted); break; } case REDWAX_TOOL_GROUP_OUT: { redwax_complete_group(r, "", state.isquoted); break; } case REDWAX_TOOL_ORDER_OUT: { redwax_complete_order_out(r, "", state.isquoted); break; } } break; } case APR_EOF: case APR_SUCCESS: { /* * Options were parsed successfully so far. Was the last * argument a bare "--"? If so, perform option completion. */ if (state.intoken == REDWAX_TOKEN_INSIDE) { redwax_tool_compgen_options(r, opt->argv[opt->ind - 1]); } break; } } return REDWAX_EXIT_OK; } static int total_modules = 0; static int max_modules = 0; static int conf_vector_length = 0; module *redwax_top_module = NULL; redwax_conf_vector_t *redwax_create_module_config(apr_pool_t *p) { return apr_pcalloc(p, sizeof(void *) * conf_vector_length); } void redwax_setup_modules(apr_pool_t *pool) { module **m; total_modules = 0; for (m = redwax_modules; *m != NULL; m++) (*m)->module_index = total_modules++; max_modules = total_modules + 1; conf_vector_length = max_modules; for (m = redwax_modules; *m != NULL; m++) { (*m)->register_hooks(pool); } } int main(int argc, const char * const argv[]) { redwax_tool_t r = { 0 }; char str[MAXHOSTNAMELEN + 1]; const char *line = NULL; apr_status_t status; int rc; euid = geteuid(); egid = getegid(); /* lets get APR off the ground, and make sure it terminates cleanly */ if (APR_SUCCESS != (status = apr_app_initialize(&argc, &argv, NULL))) { return 1; } atexit(apr_terminate); if (APR_SUCCESS != (status = apr_pool_create_ex(&r.pool, NULL, abortfunc, NULL))) { exit(REDWAX_EXIT_INIT); } if (APR_SUCCESS != (status = apr_pool_create(&r.tpool, r.pool))) { exit(REDWAX_EXIT_INIT); } /* initialise the global pool for the hook mechanism */ if (APR_SUCCESS != (status = apr_pool_create(&apr_hook_global_pool, r.pool))) { exit(REDWAX_EXIT_INIT); } redwax_setup_modules(apr_hook_global_pool); #if 0 #if HAVE_NSS_INITIALIZE redwax_add_default_nss_hooks(); #endif #if HAVE_OPENSSL_PEM_H redwax_add_default_openssl_hooks(); #endif #if HAVE_P11_KIT_MODULES_LOAD_AND_INITIALIZE redwax_add_default_p11kit_hooks(); #endif #if HAVE_LIBICAL_ICAL_H redwax_add_default_libical_hooks(); #endif #if HAVE_SECURITY_SECURITY_H redwax_add_default_keychain_hooks(); #endif redwax_add_default_hooks(); #endif apr_hook_sort_all(); #if HAVE_LIBGEN_H r.base = basename(apr_pstrdup(r.pool, argv[0])); #endif apr_file_open_stderr(&r.err, r.pool); apr_file_open_stdin(&r.in, r.pool); apr_file_open_stdout(&r.out, r.pool); r.format = REDWAX_FORMAT_YAML; r.key_in = 1; r.key_out = 1; r.cert_out = 1; r.chain_out = 1; r.auto_out = 1; r.text = 1; r.certs_in = apr_array_make(r.pool, 10, sizeof(redwax_certificate_t)); r.intermediates_in = apr_array_make(r.pool, 10, sizeof(redwax_certificate_t)); r.trusted_in = apr_array_make(r.pool, 10, sizeof(redwax_certificate_t)); r.crls_in = apr_array_make(r.pool, 10, sizeof(redwax_crl_t)); r.keys_in = apr_array_make(r.pool, 10, sizeof(redwax_key_t)); r.certs_out = apr_array_make(r.pool, 10, sizeof(redwax_certificate_t)); r.intermediates_out = apr_array_make(r.pool, 10, sizeof(redwax_certificate_t)); r.trusted_out = apr_array_make(r.pool, 10, sizeof(redwax_certificate_t)); r.crls_out = apr_array_make(r.pool, 10, sizeof(redwax_crl_t)); r.keys_out = apr_array_make(r.pool, 10, sizeof(redwax_key_t)); r.emails_index = apr_hash_make(r.pool); r.hostnames_index = apr_hash_make(r.pool); r.ips_index = apr_hash_make(r.pool); r.keys_index = apr_hash_make(r.pool); r.duplicates_index = apr_hash_make(r.pool); r.trust_duplicates_index = apr_hash_make(r.pool); r.emails = apr_hash_make(r.pool); r.hostnames = apr_hash_make(r.pool); r.ips = apr_hash_make(r.pool); r.pkcs11_in.pkcs11_modules = apr_array_make(r.pool, 10, sizeof(const char*)); r.pkcs11_out.pkcs11_modules = apr_array_make(r.pool, 10, sizeof(const char*)); if (apr_gethostname(str, sizeof(str) - 1, r.pool) != APR_SUCCESS) { apr_file_printf(r.err, "%s: could not read the servername.\n", argv[0]); r.hostname = "localhost"; } else { r.hostname = apr_pstrdup(r.pool, str); } r.home = getenv("HOME"); if (OK != rt_run_initialise(&r)) { apr_pool_destroy(r.pool); exit(REDWAX_EXIT_INIT); } /* completion? */ line = getenv(REDWAX_TOOL_COMPLINE); if (!line) { line = getenv(REDWAX_TOOL_COMMANDLINE); } if (line) { const char *cpoint = getenv(REDWAX_TOOL_COMPPOINT); /* handle limited line length */ if (cpoint) { int c = atoi(cpoint); if (c < strlen(line)) { line = apr_pstrndup(r.pool, line, c); } } r.breaks = getenv(REDWAX_TOOL_COMP_WORDBREAKS); /* handle word boundaries */ if (!r.breaks) { r.breaks = REDWAX_TOOL_COMP_WORDBREAKS_DEFAULT; } } if (line) { rc = redwax_tool_compgen(&r, line); } else { rc = redwax_tool_argv(&r, argc, argv); } apr_pool_destroy(r.pool); exit(rc); } REDWAX_DECLARE_MODULE(core) = { STANDARD_MODULE_STUFF, redwax_add_default_hooks /* register hooks */ };