]>
Commit | Line | Data |
---|---|---|
59039eaf JR |
1 | #!/bin/sh |
2 | # | |
3 | # Ben Secrest <blsecres@gmail.com> | |
4 | # | |
5 | # sh c_rehash script, scan all files in a directory | |
6 | # and add symbolic links to their hash values. | |
7 | # | |
8 | # based on the c_rehash perl script distributed with openssl | |
9 | # | |
10 | # LICENSE: See OpenSSL license | |
11 | # ^^acceptable?^^ | |
12 | # | |
13 | ||
14 | # default certificate location | |
15 | DIR=/etc/openssl | |
16 | ||
17 | # for filetype bitfield | |
18 | IS_CERT=$(( 1 << 0 )) | |
19 | IS_CRL=$(( 1 << 1 )) | |
20 | ||
21 | ||
22 | # check to see if a file is a certificate file or a CRL file | |
23 | # arguments: | |
24 | # 1. the filename to be scanned | |
25 | # returns: | |
26 | # bitfield of file type; uses ${IS_CERT} and ${IS_CRL} | |
27 | # | |
28 | check_file() | |
29 | { | |
30 | local IS_TYPE=0 | |
31 | ||
32 | # make IFS a newline so we can process grep output line by line | |
33 | local OLDIFS=${IFS} | |
34 | IFS=$( printf "\n" ) | |
35 | ||
36 | # XXX: could be more efficient to have two 'grep -m' but is -m portable? | |
37 | for LINE in $( grep '^-----BEGIN .*-----' ${1} ) | |
38 | do | |
39 | if echo ${LINE} \ | |
40 | | grep -q -E '^-----BEGIN (X509 |TRUSTED )?CERTIFICATE-----' | |
41 | then | |
42 | IS_TYPE=$(( ${IS_TYPE} | ${IS_CERT} )) | |
43 | ||
44 | if [ $(( ${IS_TYPE} & ${IS_CRL} )) -ne 0 ] | |
45 | then | |
46 | break | |
47 | fi | |
48 | elif echo ${LINE} | grep -q '^-----BEGIN X509 CRL-----' | |
49 | then | |
50 | IS_TYPE=$(( ${IS_TYPE} | ${IS_CRL} )) | |
51 | ||
52 | if [ $(( ${IS_TYPE} & ${IS_CERT} )) -ne 0 ] | |
53 | then | |
54 | break | |
55 | fi | |
56 | fi | |
57 | done | |
58 | ||
59 | # restore IFS | |
60 | IFS=${OLDIFS} | |
61 | ||
62 | return ${IS_TYPE} | |
63 | } | |
64 | ||
65 | ||
66 | # | |
67 | # use openssl to fingerprint a file | |
68 | # arguments: | |
69 | # 1. the filename to fingerprint | |
70 | # 2. the method to use (x509, crl) | |
71 | # returns: | |
72 | # none | |
73 | # assumptions: | |
74 | # user will capture output from last stage of pipeline | |
75 | # | |
76 | fingerprint() | |
77 | { | |
78 | ${SSL_CMD} ${2} -fingerprint -noout -in ${1} | sed 's/^.*=//' | tr -d ':' | |
79 | } | |
80 | ||
81 | ||
82 | # | |
83 | # link_hash - create links to certificate files | |
84 | # arguments: | |
85 | # 1. the filename to create a link for | |
86 | # 2. the type of certificate being linked (x509, crl) | |
87 | # returns: | |
88 | # 0 on success, 1 otherwise | |
89 | # | |
90 | link_hash() | |
91 | { | |
92 | local FINGERPRINT=$( fingerprint ${1} ${2} ) | |
93 | local HASH=$( ${SSL_CMD} ${2} -hash -noout -in ${1} ) | |
94 | local SUFFIX=0 | |
95 | local LINKFILE='' | |
96 | local TAG='' | |
97 | ||
98 | if [ ${2} = "crl" ] | |
99 | then | |
100 | TAG='r' | |
101 | fi | |
102 | ||
103 | LINKFILE=${HASH}.${TAG}${SUFFIX} | |
104 | ||
105 | while [ -f ${LINKFILE} ] | |
106 | do | |
107 | if [ ${FINGERPRINT} = $( fingerprint ${LINKFILE} ${2} ) ] | |
108 | then | |
109 | printf "WARNING: Skipping duplicate file ${1}\n" >&2 | |
110 | return 1 | |
111 | fi | |
112 | ||
113 | SUFFIX=$(( ${SUFFIX} + 1 )) | |
114 | LINKFILE=${HASH}.${TAG}${SUFFIX} | |
115 | done | |
116 | ||
117 | printf "${1} => ${LINKFILE}\n" | |
118 | ||
119 | # assume any system with a POSIX shell will either support symlinks or | |
120 | # do something to handle this gracefully | |
121 | ln -s ${1} ${LINKFILE} | |
122 | ||
123 | return 0 | |
124 | } | |
125 | ||
126 | ||
127 | # hash_dir create hash links in a given directory | |
128 | hash_dir() | |
129 | { | |
130 | printf "Doing ${1}\n" | |
131 | ||
132 | cd ${1} | |
133 | ||
134 | for FILE in * | |
135 | do | |
136 | # no files in directory at all, no point in continuing | |
137 | if ! [ -f ${FILE} ] | |
138 | then | |
139 | return 1 | |
140 | fi | |
141 | ||
142 | if echo ${FILE} | grep -q -E '^[[:xdigit:]]{8}\.r?[[:digit:]]+$' \ | |
143 | && [ -h "${FILE}" ] | |
144 | then | |
145 | rm ${FILE} | |
146 | fi | |
147 | done | |
148 | ||
149 | for FILE in *.pem | |
150 | do | |
151 | # no pem files so FILE gets set to the unexpanded *.pem | |
152 | if ! [ -f ${FILE} ] | |
153 | then | |
154 | break | |
155 | fi | |
156 | ||
157 | check_file ${FILE} | |
158 | local FILE_TYPE=${?} | |
159 | local TYPE_STR='' | |
160 | ||
161 | if [ $(( ${FILE_TYPE} & ${IS_CERT} )) -ne 0 ] | |
162 | then | |
163 | TYPE_STR='x509' | |
164 | elif [ $(( ${FILE_TYPE} & ${IS_CRL} )) -ne 0 ] | |
165 | then | |
166 | TYPE_STR='crl' | |
167 | else | |
168 | printf "WARNING: ${FILE} does not contain a certificate or CRL: skipping\n" >&2 | |
169 | continue | |
170 | fi | |
171 | ||
172 | link_hash ${FILE} ${TYPE_STR} | |
173 | done | |
174 | } | |
175 | ||
176 | ||
177 | # choose the name of an ssl application | |
178 | if [ -n "${OPENSSL}" ] | |
179 | then | |
180 | SSL_CMD=${OPENSSL} | |
181 | else | |
182 | SSL_CMD=openssl | |
183 | OPENSSL=${SSL_CMD} | |
184 | export ${OPENSSL} | |
185 | fi | |
186 | ||
187 | # fix paths | |
188 | PATH=${PATH}:${DIR}/bin | |
189 | export PATH | |
190 | ||
191 | # confirm existance/executability of ssl command | |
192 | if ! [ -x $( which ${SSL_CMD} ) ] | |
193 | then | |
194 | printf "${0}: rehashing skipped ('openssl' program not available)\n" >&2 | |
195 | exit 0 | |
196 | fi | |
197 | ||
198 | # determine which directories to process | |
4d0769bb | 199 | old_IFS=$IFS |
59039eaf JR |
200 | if [ ${#} -gt 0 ] |
201 | then | |
4d0769bb | 202 | IFS=':' |
59039eaf JR |
203 | DIRLIST=${*} |
204 | elif [ -n "${SSL_CERT_DIR}" ] | |
205 | then | |
4d0769bb | 206 | DIRLIST=$SSL_CERT_DIR |
59039eaf JR |
207 | else |
208 | DIRLIST=${DIR}/certs | |
209 | fi | |
210 | ||
4d0769bb JR |
211 | IFS=':' |
212 | ||
59039eaf JR |
213 | # process directories |
214 | for CERT_DIR in ${DIRLIST} | |
215 | do | |
216 | if [ -d ${CERT_DIR} -a -w ${CERT_DIR} ] | |
217 | then | |
4d0769bb | 218 | IFS=$old_IFS |
59039eaf | 219 | hash_dir ${CERT_DIR} |
4d0769bb | 220 | IFS=':' |
59039eaf JR |
221 | fi |
222 | done |