ldapsearch & openssl で 証明書有効期限チェックスクリプト

# 要件

 - LDAP に格納されているアカウントの証明書の有効期限をチェックしたい
 - スクリプトは cron で定期的に実行したい
 - 有効期限が切れて、一定期間経ったアカウントはチェック対象外としたい
 - 使用可能ライブラリは、`ldapsearch`, `openssl`, `ShellScript`。ライブラリ追加不可。
 - LDAP は `anonymous` バインド可能

# 実行環境

- Red Hat 4.4.7-16
- OpenSSL 1.0.1e-fips 11 Feb 2013
- ldapsearch 2.4.40

# ソースコード

```CertCheck.sh
#!/bin/sh
#set -x

# カレントディレクトリで作業する
cd `dirname $0`

# 接続先(外部引数)
LDAPHOST="ldaps://hogehoge:636"
# 検索ベースDN(サーバーリスト)
BASEDN="ou=servers,ou=pki,o=Hoge"

# 全件処理対象リスト
ALL_LIST_TXT=all.txt
# 証明書ファイル
CERT_FILE_TXT=cert.txt
# 結果ファイル
RESULT_TXT=result.txt
# 有効期限日数(この日数以内に証明書が切れる場合は処理対象)
EXPIRATION_DATES=60
# 期限切れ無視日数(この日数をすぎた証明書は無視)
EXPIRED_DATES=30

# 事前準備 ------------

if [ -f $CERT_FILE_TXT ]
then
	rm -f $CERT_FILE_TXT
fi

if [ -f $RESULT_TXT ]
then
	rm -f $RESULT_TXT
fi

if [ -f $ALL_LIST_TXT ]
then
	rm -f $ALL_LIST_TXT
fi

# チェック対象全件リスト生成 ------------
/usr/bin/ldapsearch -LLL -H $LDAPHOST -x -b $BASEDN "(cn=*)" cn > $ALL_LIST_TXT

# チェック対象全件リストから証明書をファイル保存 ------------
while read line
do
	# 全件リストのうち、cnをチェック
	if echo $line | grep "cn:" >/dev/null ; then
		echo "cn: $line"
		
		# 属性名(4文字より前)は除去して変数として保持
		CVAR=(cn="${line:4}")
		# 証明書バイナリだけを別ファイルとして保存(tmp以下に出来る)
		#    -LLL       print responses in LDIF format without comments
		#             and version
		#    -H URI     LDAP Uniform Resource Identifier(s)
		#    -x         Simple authentication
		#    -b basedn  base dn for search
		#    -u         include User Friendly entry names in the output
		#    -t         write binary values to files in temporary directory
		/usr/bin/ldapsearch -LLL -H $LDAPHOST -x -b $BASEDN -u -t "($CVAR)"  "usercertificate;binary"
	fi
done < $ALL_LIST_TXT


# 終わったら、証明書のバイナリを作業フォルダに移動 ------------
mv "/tmp/ldapsearch-usercertificate;binary-"* ./

# 証明書一件ずつ有効期限チェック ------------
for file in `\find . -maxdepth 1 -name 'ldapsearch-usercertificate;binary-*'`; do
    echo $file ----------------
    
    # 証明書の中身をder形式で読み出し、ファイルに出力(上書きで1件だけ)
	#    x509            - 署名済み証明書
	#    -in arg         - input file - default stdin
	#    -text           - print the certificate in text form
	#    -inform arg     - input format - default PEM (one of DER, NET or PEM)
    openssl x509 -in $file  -text  -inform der > $CERT_FILE_TXT

	# 出力したファイルの中身をチェック
	while read line; do
	    #echo $line
	    
	    # 有効期限日
    	if echo $line | grep "Not After" >/dev/null ; then
    		# 属性名は除外
    		notafter=${line:12}
		fi
	    
	    # Subject
    	if echo $line | grep "Subject" >/dev/null; then
    		# 属性名は除外
    		subject=${line:9}

			echo $notafter
			echo $subject

			# 有効期限の秒数を取得
			notafter_second=`date -d "$notafter" '+%s'`
			# 指定日数後の秒数を取得
			nextdate_second=`date -d "$EXPIRATION_DATES days" '+%s'`
			# 期限切れ日数後の秒数を取得
			expireddate_second=`date -d "-$EXPIRED_DATES days" '+%s'`
			# 本日の秒数を取得
			now_second=`date '+%s'`
			
			# echo notafter_second: $notafter_second
			# echo nextdate_second: $nextdate_second
			# echo now_second: $now_second
			# echo expireddate_second: $expireddate_second
			
			if [ $nextdate_second -ge $notafter_second ] && [ $expireddate_second -le $notafter_second ] ; then
				# 有効期限が指定日数後以下(指定日数以内に期限が切れる)場合
				# かつ、期限切れで指定日数以降ではない場合
				echo $subject >> $RESULT_TXT
				echo 有効期限: $notafter >> $RESULT_TXT
				
				diff=`expr \( $notafter_second - $now_second \) / \( 60 \* 60 \* 24 \)`
				# echo diff: $diff
				
				echo 残り日数: $diff >> $RESULT_TXT
				echo "" >> $RESULT_TXT
			fi
			
			# subjectが取れたら終了
			break
		fi
		
	done < $CERT_FILE_TXT

    # 終わったら証明書削除
    rm -f $file
done

# 後始末 ------------
if [ -f $CERT_FILE_TXT ]
then
	rm $CERT_FILE_TXT
fi

if [ -f $ALL_LIST_TXT ]
then
	rm $ALL_LIST_TXT
fi
```

# 参考
- [LDAP にユーザー証明書を保管する](http://publib.boulder.ibm.com/tividd/td/ITAMFBI/SC23-4831-01/ja_JA/HTML/ADM51mst160.htm)
- [opensslコマンド例文集 - てくにかるむ // multix.jp](http://multix.jp/openssl-command/)