#!/bin/sh # # Converts NSS certdata objects text into a concatenated PEM bundle. # # Copyright (C) 2020 Patrick McDermott # # This program is free software: you can redistribute it and/or modify # it under the terms of the GNU General Public License as published by # the Free Software Foundation, either version 3 of the License, or # (at your option) any later version. # # This program is distributed in the hope that it will be useful, # but WITHOUT ANY WARRANTY; without even the implied warranty of # MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the # GNU General Public License for more details. # # You should have received a copy of the GNU General Public License # along with this program. If not, see . set -eu error() { local fmt="${1}" shift 1 printf "Error: ${fmt}\n" "${@}" 1>&2 } parse() { local txt_input="${1}" local trust_type="${2}" local pem_output="${3}" shift 3 local attr= local type= local val= local class= local label= local value= local trust= local trusted= if [ x"${txt_input}" != x'-' ]; then exec 0<"${txt_input}" fi if [ x"${pem_output}" != x'-' ]; then exec 1>"${pem_output}" fi while read -r attr && [ x"${attr}" != x'BEGINDATA' ]; do :; done while read -r attr type val; do case "${attr}" in '#'*) continue ;; 'CKA_CLASS') class="${val}" ;; 'CKA_LABEL') val="${val#\"}" val="${val%\"}" label="$(printf '%s' "${val}" | \ tr -C 'A-Za-z0-9_' '_')" ;; 'CKA_VALUE') if [ x"${type}" != x'MULTILINE_OCTAL' ]; then error 'Unsupported value type' return 1 fi value= while read -r val; do case "${val}" in 'END') break ;; *[!0-7\\]*) error "Invalid $(: \ )multiline $(: \ )octal value" return 1 ;; *) value="${value}${val}" ;; esac done ;; "CKA_TRUST_${trust_type}") trust="${val}" ;; '') if [ -z "${class}" ]; then error 'Object missing class' return 1 fi if [ -z "${label}" ]; then error 'Object missing label' return 1 fi if [ x"${class}" = x'CKO_CERTIFICATE' ]; then if [ -z "${value}" ]; then error 'Object missing value' return 1 fi eval "certificate_${label}=\${value}" elif [ x"${class}" = x'CKO_NSS_TRUST' ]; then if [ -z "${trust}" ]; then error 'Object missing trust' return 1 fi if [ x"${trust}" = \ x'CKT_NSS_TRUSTED_DELEGATOR' ] then trusted="${trusted} ${label}" fi elif [ x"${class}" = \ x'CKO_NSS_BUILTIN_ROOT_LIST' ] then : else error 'Unknown class "%s"' "${val}" return 1 fi class= label= value= trust= esac done <<-EOF $(cat) EOF # Extra newline in input to trigger final empty line block in parser. for label in ${trusted}; do printf '%s\n' '-----BEGIN CERTIFICATE-----' eval "printf \"\${certificate_${label}}\"" | base64 - | \ tr -d '\n' | fold -w 64 - printf '\n%s\n' '-----END CERTIFICATE-----' done return 0 } usage() { printf 'Usage: %s \n' "${0}" printf 'Example: %s certdata.txt SERVER_AUTH certdata.pem\n' "${0}" return 0 } main() { local txt_input= local trust_type= local pem_output= if [ ${#} -ne 3 ]; then usage 1>&2 return 1 fi txt_input="${1}" trust_type="${2}" pem_output="${3}" shift 3 parse "${txt_input}" "${trust_type}" "${pem_output}" || return 1 return 0 } main "${@}"