Check-in [3244026fd6]
Overview
Comment:Added working certificate support
Downloads: Tarball | ZIP archive | SQL archive
Timelines: family | ancestors | descendants | both | trunk
Files: files | file ages | folders
SHA1:3244026fd617fe4f6763765ed5ff9eea02272888
User & Date: rkeene on 2014-11-17 20:37:58
Other Links: manifest | tags
Context
2014-11-17
20:50
Updated to trim trailing newlines check-in: 3242c8d4d5 user: rkeene tags: trunk
20:37
Added working certificate support check-in: 3244026fd6 user: rkeene tags: trunk
20:37
Updated to include entire error stack on error for --tcl mode in AppFSd check-in: 4b2e0bf187 user: rkeene tags: trunk
Changes

Modified .fossil-settings/ignore-glob from [efc67b31c0] to [09c745f86a].

     1      1   appfsd
     2      2   appfsd.o
     3      3   appfsd.tcl.h
     4      4   sha1.o
     5      5   sha1.tcl.h
     6      6   pki.tcl.h
     7      7   pki.tcl.new
            8  +pki.tcl
            9  +CA

Added appfs-cert version [536bd4fe36].

            1  +#! /usr/bin/env bash
            2  +
            3  +appfsd_options=()
            4  +
            5  +CA_CERT_FILE='AppFS_CA.crt'
            6  +CA_KEY_FILE='AppFS_CA.key'
            7  +export CA_CERT_FILE CA_KEY_FILE
            8  +
            9  +function call_appfsd() {
           10  +	appfsd "${appfsd_options[@]}" "$@"
           11  +}
           12  +
           13  +function read_password() {
           14  +	local prompt variable
           15  +
           16  +	prompt="$1"
           17  +	variable="$2"
           18  +
           19  +	if [ -z "$(eval echo '$'${variable})" ]; then
           20  +		echo -n "${prompt}" >&2
           21  +
           22  +		stty -echo
           23  +		IFS='' read -r $variable
           24  +		stty echo
           25  +		echo '' >&2
           26  +	fi
           27  +}
           28  +
           29  +function read_text() {
           30  +	local prompt variable
           31  +
           32  +	prompt="$1"
           33  +	variable="$2"
           34  +
           35  +	if [ -z "$(eval echo '$'${variable})" ]; then
           36  +		echo -n "${prompt}" >&2
           37  +
           38  +		IFS='' read -r $variable
           39  +	fi
           40  +}
           41  +
           42  +function generate_ca_cert_and_key() {
           43  +	read_text 'Certificate Authority (CA) Company Name (O): ' CA_DN_S_O
           44  +	read_text 'Certificate Authority (CA) Responsible Party Name (CN): ' CA_DN_S_CN
           45  +	read_password 'Password for Certificate Authority Key: ' CA_PASSWORD
           46  +
           47  +	export CA_DN_S_O CA_DN_S_CN CA_PASSWORD
           48  +
           49  +	call_appfsd --tcl '
           50  +package require pki
           51  +
           52  +set filename_cert $::env(CA_CERT_FILE)
           53  +set filename_key  $::env(CA_KEY_FILE)
           54  +
           55  +puts -nonewline "Generating RSA Key..."
           56  +flush stdout
           57  +set key [pki::rsa::generate 2048]
           58  +puts " Done."
           59  +
           60  +lappend key subject "O=$::env(CA_DN_S_O),CN=$::env(CA_DN_S_CN)"
           61  +
           62  +set ca [pki::x509::create_cert $key $key 1 [clock seconds] [clock add [clock seconds] 5 years] 1 [list] 1]
           63  +
           64  +puts "Writing \"$filename_cert\""
           65  +set fd [open $filename_cert w 0644]
           66  +puts $fd $ca
           67  +close $fd
           68  +
           69  +puts "Writing \"$filename_key\""
           70  +set fd [open $filename_key w 0400]
           71  +puts $fd [pki::key $key $::env(CA_PASSWORD)]
           72  +close $fd
           73  +'
           74  +}
           75  +
           76  +function generate_key() {
           77  +	read_password 'Password for Site Key: ' SITE_PASSWORD
           78  +
           79  +	export SITE_PASSWORD
           80  +
           81  +	call_appfsd --tcl '
           82  +package require pki
           83  +
           84  +if {[info exists ::env(SITE_KEY_FILE)]} {
           85  +	set filename_key $::env(SITE_KEY_FILE)
           86  +} else {
           87  +	set filename_key "AppFS_Site.key"
           88  +}
           89  +
           90  +puts -nonewline "Generating RSA Key..."
           91  +flush stdout
           92  +set key [pki::rsa::generate 2048]
           93  +puts " Done."
           94  +
           95  +puts "Writing \"$filename_key\""
           96  +set fd [open $filename_key w 0400]
           97  +puts $fd [pki::key $key $::env(SITE_PASSWORD)]
           98  +close $fd
           99  +'
          100  +}
          101  +
          102  +function generate_csr() {
          103  +	read_text 'Site hostname: ' SITE_HOSTNAME
          104  +
          105  +	if [ -z "${SITE_KEY_FILE}" ]; then
          106  +		SITE_KEY_FILE="AppFS_Site_${SITE_HOSTNAME}.key"
          107  +	fi
          108  +
          109  +	export SITE_HOSTNAME SITE_KEY_FILE
          110  +
          111  +	if [ -f "${SITE_KEY_FILE}" ]; then
          112  +		echo 'Key file already exists.'
          113  +		read_password 'Password for (existing) Site Key: ' SITE_PASSWORD
          114  +
          115  +		export SITE_PASSWORD
          116  +	else
          117  +		generate_key
          118  +	fi
          119  +
          120  +call_appfsd --tcl '
          121  +package require pki
          122  +
          123  +if {[info exists ::env(SITE_KEY_FILE)]} {
          124  +        set filename_key $::env(SITE_KEY_FILE)
          125  +} else {
          126  +        set filename_key "AppFS_Site.key"
          127  +}
          128  +set filename_csr "[file rootname $filename_key].csr"
          129  +
          130  +set key [read [open $filename_key]]
          131  +
          132  +set key [::pki::pkcs::parse_key $key $::env(SITE_PASSWORD)]
          133  +
          134  +set csr [::pki::pkcs::create_csr $key [list CN $::env(SITE_HOSTNAME)] 1]
          135  +
          136  +puts "Writing \"$filename_csr\""
          137  +set fd [open $filename_csr w 0644]
          138  +puts $fd $csr
          139  +close $fd
          140  +'
          141  +}
          142  +
          143  +function generate_cert() {
          144  +	SITE_CSR_FILE="$1"
          145  +
          146  +	read_text 'Certificate Signing Request (CSR) file: ' SITE_CSR_FILE
          147  +
          148  +	if [ -z "${SITE_CSR_FILE}" ]; then
          149  +		generate_csr || exit 1
          150  +
          151  +		SITE_CSR_FILE="$(echo "${SITE_KEY_FILE}" | sed 's@.[^\.]*$@@').csr"
          152  +	fi
          153  +
          154  +	if [ ! -e "${CA_CERT_FILE}" -o ! -e "${CA_KEY_FILE}" ]; then
          155  +		read_text 'Certificate Authority (CA) Certificate Filename: ' CA_CERT_FILE
          156  +		read_text 'Certificate Authority (CA) Key Filename: ' CA_KEY_FILE
          157  +	fi
          158  +
          159  +	read_password 'Certificate Authority (CA) Password: ' CA_PASSWORD
          160  +
          161  +	SITE_SERIAL_NUMBER="$(uuidgen | dd conv=ucase 2>/dev/null | sed 's@-@@g;s@^@ibase=16; @' | bc -lq)"
          162  +
          163  +	export SITE_CSR_FILE SITE_SERIAL_NUMBER CA_CERT_FILE CA_KEY_FILE CA_PASSWORD
          164  +
          165  +	SITE_CERT="$(call_appfsd --tcl '
          166  +package require pki
          167  +
          168  +set csr [read [open $::env(SITE_CSR_FILE)]]
          169  +set csr [::pki::pkcs::parse_csr $csr]
          170  +
          171  +set ca_key [read [open $::env(CA_KEY_FILE)]]
          172  +set ca_cert [read [open $::env(CA_CERT_FILE)]]
          173  +
          174  +set ca_key [::pki::pkcs::parse_key $ca_key $::env(CA_PASSWORD)]
          175  +set ca_cert [::pki::x509::parse_cert $ca_cert]
          176  +set ca_key [concat $ca_key $ca_cert]
          177  +
          178  +set cert [::pki::x509::create_cert $csr $ca_key $::env(SITE_SERIAL_NUMBER) [clock seconds] [clock add [clock seconds] 1 year] 0 [list] 1]
          179  +
          180  +puts $cert
          181  +')"
          182  +
          183  +	SITE_SUBJECT="$(echo "${SITE_CERT}" | openssl x509 -subject -noout | sed 's@.*= @@')"
          184  +
          185  +	echo "${USER}@${HOSTNAME} $(date): ${SITE_SERIAL_NUMBER} ${SITE_SUBJECT}" >> "${CA_KEY_FILE}.issued"
          186  +
          187  +	echo "${SITE_CERT}"
          188  +}
          189  +
          190  +function generate_selfsigned() {
          191  +	read_password 'Password for Key: ' SITE_PASSWORD
          192  +	read_text 'Site hostname: ' SITE_HOSTNAME
          193  +
          194  +	SITE_SERIAL_NUMBER="$(uuidgen | dd conv=ucase 2>/dev/null | sed 's@-@@g;s@^@ibase=16; @' | bc -lq)"
          195  +
          196  +	export SITE_PASSWORD SITE_HOSTNAME SITE_SERIAL_NUMBER
          197  +
          198  +	call_appfsd --tcl '
          199  +package require pki
          200  +
          201  +set filename_cert "AppFS_Site_$::env(SITE_HOSTNAME).crt"
          202  +set filename_key  "AppFS_Site_$::env(SITE_HOSTNAME).key"
          203  +
          204  +puts -nonewline "Generating RSA Key..."
          205  +flush stdout
          206  +set key [pki::rsa::generate 2048]
          207  +puts " Done."
          208  +
          209  +lappend key subject "CN=$::env(SITE_HOSTNAME)"
          210  +
          211  +set cert [pki::x509::create_cert $key $key $::env(SITE_SERIAL_NUMBER) [clock seconds] [clock add [clock seconds] 1 years] 1 [list] 1]
          212  +
          213  +puts "Writing \"$filename_cert\""
          214  +set fd [open $filename_cert w 0644]
          215  +puts $fd $cert
          216  +close $fd
          217  +
          218  +puts "Writing \"$filename_key\""
          219  +set fd [open $filename_key w 0400]
          220  +puts $fd [pki::key $key $::env(SITE_PASSWORD)]
          221  +close $fd
          222  +'
          223  +}
          224  +
          225  +function sign_site() {
          226  +	SITE_INDEX_FILE="$1"
          227  +	SITE_KEY_FILE="$2"
          228  +	SITE_CERT_FILE="$3"
          229  +
          230  +	read_text 'AppFS Site Index file: ' SITE_INDEX_FILE
          231  +	read_text 'Site Key filename: ' SITE_KEY_FILE
          232  +	read_text 'Site Certificate filename: ' SITE_CERT_FILE
          233  +	read_password "Password for Key (${SITE_KEY_FILE}): " SITE_PASSWORD
          234  +
          235  +	export SITE_INDEX_FILE SITE_KEY_FILE SITE_CERT_FILE SITE_PASSWORD
          236  +
          237  +	call_appfsd --tcl "$(cat <<\_EOF_
          238  +package require pki
          239  +
          240  +set fd [open $::env(SITE_INDEX_FILE)]
          241  +gets $fd line
          242  +close $fd
          243  +
          244  +set line [split $line ","]
          245  +
          246  +# Data to be signed
          247  +set data [join [lrange $line 0 1] ","]
          248  +
          249  +set key [read [open $::env(SITE_KEY_FILE)]]
          250  +set key [::pki::pkcs::parse_key $key $::env(SITE_PASSWORD)]
          251  +
          252  +set cert [read [open $::env(SITE_CERT_FILE)]]
          253  +array set cert_arr [::pki::_parse_pem $cert "-----BEGIN CERTIFICATE-----" "-----END CERTIFICATE-----"]
          254  +binary scan $cert_arr(data) H* cert
          255  +
          256  +set signature [::pki::sign $data $key]
          257  +binary scan $signature H* signature
          258  +
          259  +set data [split $data ","]
          260  +lappend data $cert
          261  +lappend data $signature
          262  +
          263  +set data [join $data ","]
          264  +
          265  +set fd [open "$::env(SITE_INDEX_FILE).new" "w"]
          266  +puts $fd $data
          267  +close $fd
          268  +
          269  +file rename -force -- "$::env(SITE_INDEX_FILE).new" $::env(SITE_INDEX_FILE)
          270  +
          271  +_EOF_
          272  +)"
          273  +}
          274  +
          275  +cmd="$1"
          276  +shift
          277  +case "${cmd}" in
          278  +	generate-ca)
          279  +		generate_ca_cert_and_key "$@" || exit 1
          280  +		;;
          281  +	generate-key)
          282  +		# Hidden, users should use "generate-csr" instead
          283  +		generate_key "$@" || exit 1
          284  +		;;
          285  +	generate-csr)
          286  +		generate_csr "$@" || exit 1
          287  +		;;
          288  +	sign-csr|generate-cert)
          289  +		generate_cert "$@" || exit 1
          290  +		;;
          291  +	generate-selfsigned)
          292  +		generate_selfsigned "$@" || exit 1
          293  +		;;
          294  +	sign-site)
          295  +		sign_site "$@" || exit 1
          296  +		;;
          297  +	*)
          298  +		echo 'Usage: appfs-cert {generate-selfsigned|generate-ca|generate-csr|sign-csr|generate-cert|sign-site}' >&2
          299  +
          300  +		exit 1
          301  +		;;
          302  +esac
          303  +
          304  +exit 0

Modified appfsd.tcl from [2e09ba053f] to [af3233ef21].

    20     20   	}
    21     21   }
    22     22   
    23     23   namespace eval ::appfs {
    24     24   	variable cachedir "/tmp/appfs-cache"
    25     25   	variable ttl 3600
    26     26   	variable nttl 60
    27         -
           27  +	variable trusted_cas [list]
    28     28   
    29     29   	proc _hash_sep {hash {seps 4}} {
    30     30   		for {set idx 0} {$idx < $seps} {incr idx} {
    31     31   			append retval "[string range $hash [expr {$idx * 2}] [expr {($idx * 2) + 1}]]/"
    32     32   		}
    33     33   		append retval "[string range $hash [expr {$idx * 2}] end]"
    34     34   
................................................................................
    93     93   		if {![regexp {^[0-9a-f]*$} $value]} {
    94     94   			return false
    95     95   		}
    96     96   
    97     97   		return true
    98     98   	}
    99     99   
   100         -	proc _verifySignatureAndCertificate {certificate signature} {
          100  +	proc _verifySignatureAndCertificate {hostname certificate signature hash} {
          101  +		set certificate [binary format "H*" $certificate]
          102  +		set signature   [binary format "H*" $signature]
          103  +
          104  +		set certificate [::pki::x509::parse_cert $certificate]
          105  +
          106  +		array set certificate_arr $certificate
          107  +		set certificate_cn [::pki::x509::_dn_to_cn $certificate_arr(subject)]
          108  +
          109  +		if {![::pki::verify $signature "$hash,sha1" $certificate]} {
          110  +			return false
          111  +		}
          112  +
          113  +		if {[string tolower $certificate_cn] != [string tolower $hostname]} {
          114  +			return false
          115  +		}
          116  +
          117  +		if {![::pki::x509::verify_cert $certificate $::appfs::trusted_cas]} {
          118  +			return false
          119  +		}
          120  +
   101    121   		return true
   102    122   	}
   103    123   
   104    124   	proc _normalizeOS {os} {
   105    125   		set os [string tolower [string trim $os]]
   106    126   
   107    127   		switch -- $os {
................................................................................
   148    168   	}
   149    169   
   150    170   	proc init {} {
   151    171   		if {[info exists ::appfs::init_called]} {
   152    172   			return
   153    173   		}
   154    174   
   155         -		# Force [parray] to be loaded
          175  +		# Force [parray] and [clock] to be loaded
   156    176   		catch {
   157    177   			parray does_not_exist
   158    178   		}
          179  +		catch {
          180  +			clock seconds
          181  +		}
          182  +		catch {
          183  +			clock add [clock seconds] 3 seconds
          184  +		}
   159    185   
   160    186   		set ::appfs::init_called 1
          187  +
          188  +		# Add a default CA to list of trusted CAs
          189  +		lappend ::appfs::trusted_cas [::pki::x509::parse_cert {
          190  +-----BEGIN CERTIFICATE-----
          191  +MIIC7DCCAdSgAwIBAgIBATANBgkqhkiG9w0BAQUFADAvMRIwEAYDVQQKEwlSb3kg
          192  +S2VlbmUxGTAXBgNVBAMTEEFwcEZTIEtleSBNYXN0ZXIwHhcNMTQxMTE3MjAxNzI4
          193  +WhcNMTkxMTE3MjAxNzI4WjAvMRIwEAYDVQQKEwlSb3kgS2VlbmUxGTAXBgNVBAMT
          194  +EEFwcEZTIEtleSBNYXN0ZXIwggEiMA0GCSqGSIb3DQEBAQUAA4IBDwAwggEKAoIB
          195  +AQCq6uSK46yG5b6RJWwRlvw5glAnjsc1GiX3duXA0vG4qnKUnDtl/jcMmq2GMOB9
          196  +Iy1tjabEHA0MhW2j7Vwe/O9MLFJkJ30M1PVD7YZRRNaAsz3UWIKEjPI7BBc32KOm
          197  +BL3CTXCCdzllL1HhVbnM5iCAmgHcg1DUk/EvWXvnEDxXRy2lV9mQsmDedrffY7Wl
          198  +Or57nlczaMuPLpyRSkv75PAnjQJxT3sWlBpy+/H9ImudQdpJNf/FtxcqN7iDwH5B
          199  +vIceYEtDVxFsvo5HOVkSl9jeo5E4Gpe3wyfRhoqB2UkaW1Kq0iH5R+00S760xQMx
          200  +LL9L1duhu1dL7HsmEw7IeYURAgMBAAGjEzARMA8GA1UdEwEB/wQFMAMBAf8wDQYJ
          201  +KoZIhvcNAQEFBQADggEBAKhO4ZSzYP37BqixNHKK9+gSeC6Fga85iLWhwpPW0kSl
          202  +z03hal80KZ+kPMzb8C52N283tQNAqJ9Q8akDPZxSzzMUVOGpGw2pJ7ZswKDz0ZTa
          203  +0edq/gdT/HrdegvNtDPc2jona5FVOYqwdcz5kbl1UWBaBp3VXUgcYjXSRaBK43Wd
          204  +cveiDUeZw7gHqRSN/AyYUCtJzWmvGsJuIFhMBonuz8jylhyMJCYJFT4iMUC8MNIw
          205  +niX1xx+Nu6fPV5ZZHj9rbhiBaLjm+tkDwtPgA3j2pxvHKYptuWxeYO+9DDNa9sCb
          206  +E5AnJIlOnd/tGe0Chf0sFQg+l9nNiNrWGgzdd9ZPJK4=
          207  +-----END CERTIFICATE-----
          208  +}]
   161    209   
   162    210   		# Load configuration file
   163    211   		set config_file [file join $::appfs::cachedir config]
   164    212   		if {[file exists $config_file]} {
   165    213   			source $config_file
   166    214   		}
   167    215   
................................................................................
   239    287   		set indexhashcert   [lindex $indexhash_data 2]
   240    288   		set indexhashsig    [lindex $indexhash_data 3]
   241    289   
   242    290   		if {![_isHash $indexhash]} {
   243    291   			return -code error "Invalid hash: $indexhash"
   244    292   		}
   245    293   
   246         -		if {![_verifySignatureAndCertificate $indexhashcert $indexhashsig]} {
          294  +		if {![_verifySignatureAndCertificate $hostname $indexhashcert $indexhashsig $indexhash]} {
   247    295   			return -code error "Invalid signature or certificate from $hostname"
   248    296   		}
   249    297   
   250    298   		set file [download $hostname $indexhash]
   251    299   		set fd [open $file]
   252    300   		set data [read $fd]
   253    301   		close $fd