Windows環境のRedmineにて、RedmineのユーザとSubversionのユーザを同期して使用したい(5)
いよいよsha1でパスワードをハッシュ化する実装をmod_authn_mysqlに実装し、Redmineのユーザデータベースの内容と
一致させ、subversionの認証時にもRedmineのユーザ情報を利用するという目的を果たせる段階まできました。
問題のsha1の実装ですが、「sha1」「rfc」で検索するとhttp://www.faqs.org/rfcs/rfc3174.htmlというページがヒットします。
ここには、sha1のCの実装サンプルが含まれているので、これをそのまま利用することにします。
この文章の第七章に、C言語のサンプルが書かれているのですが、ヘッダファイルはそのままだとVCのコンパイルが
通りません。stdint.hがないためです。
しかし、sha1.hには以下のようにコメントが書かれているので、
/* * If you do not have the ISO standard stdint.h header file, then you * must typdef the following: * name meaning * uint32_t unsigned 32 bit integer * uint8_t unsigned 8 bit integer (i.e., unsigned char) * int_least16_t integer of >= 16 bits * */
#include
typedef unsigned long uint32_t; typedef unsigned char uint8_t; typedef short int_least16_t;
これでsha1は使用可能です。
以下のようなソースで文字列(パスワード)をsha1でハッシュ化できます。
下のソースのsha1_passwordにハッシュ化されたパスワードが格納されます。
SHA1Context sha; int i, err; uint8_t Message_Digest[20]; char sha1_password[60] = ""; char tmp[10] = ""; err = SHA1Reset(&sha); if (err) { fprintf(stderr, "SHA1Reset Error %d.\n", err); } err = SHA1Input(&sha,(const unsigned char *) password ,strlen(password)); if (err) { fprintf(stderr, "SHA1Input Error %d.\n", err); } err = SHA1Result(&sha, Message_Digest); if (err) { fprintf(stderr,"SHA1Result Error %d, could not compute message digest.\n",err); } else { for (i = 0; i < 20 ; ++i) { sprintf(tmp, "%02x", Message_Digest[i] ); tmp[2] = '\0'; strcat(sha1_password, tmp); } }
肝心のmod_authn_mysqlの変更に移ります。
HTTP経由でsvnにアクセスした際にパスワードを聞かれますが、このパスワードは、mod_authn_mysqlのcheck_mysql_pwに渡されてきます。
check_mysql_pwの引数の変数[password]がユーザが入力したパスワードです。
そして、mysql_passwordがRedmineのusersテーブルから取得したハッシュ化されたパスワードです。
下に掲載したプログラムの下から数えて14行目あたりにコメントアウトされた行があると思います。
ここで比較が行われていますので、その前に変数[password]をハッシュ化すればよいと思います。
以下に、sha1のハッシュ化を組み込んだcheck_mysql_pwをすべて載せておきます。
static authn_status check_mysql_pw(request_rec *r, const char *user, const char *password) { SHA1Context sha; int i, err; uint8_t Message_Digest[20]; char sha1_password[60] = ""; char tmp[10] = ""; authn_status ARV = AUTH_DENIED; int blah = 0; mysql_config *conf; mysql_dconfig *dconf = ap_get_module_config(r->per_dir_config,&authn_mysql_module); char* mysql_password; char* query; MYSQL_ROW sql_row; MYSQL_RES *result = NULL; mysql_res *mysql_res; const char* esc_user = mysql_escape(user, r->pool); conf = apr_hash_get(authn_mysql_config, dconf->id, APR_HASH_KEY_STRING); if(conf == NULL) { ap_log_rerror(APLOG_MARK, APLOG_ERR, 0, r, "[mod_authn_mysql.c] - Server Config for \"%s\" was not found", dconf->id); return ARV; } apr_reslist_acquire(conf->pool, (void **)&mysql_res); /* password translate from clear text to sha1 */ err = SHA1Reset(&sha); if (err) { fprintf(stderr, "SHA1Reset Error %d.\n", err); } err = SHA1Input(&sha,(const unsigned char *) password ,strlen(password)); if (err) { fprintf(stderr, "SHA1Input Error %d.\n", err); } err = SHA1Result(&sha, Message_Digest); if (err) { fprintf(stderr,"SHA1Result Error %d, could not compute message digest.\n",err); } else { for (i = 0; i < 20 ; ++i) { sprintf(tmp, "%02x", Message_Digest[i] ); tmp[2] = '\0'; strcat(sha1_password, tmp); } } /* make the query to get the user's password */ if (conf->rec.isactive_field) { query = apr_psprintf(r->pool, "SELECT %s FROM %s WHERE %s='%s' AND %s!=0 LIMIT 0,1", conf->rec.password_field, conf->rec.mysql_table, conf->rec.username_field, esc_user, conf->rec.isactive_field); } else { query = apr_psprintf(r->pool, "SELECT %s FROM %s WHERE %s='%s' AND %s!=0 LIMIT 0,1", conf->rec.password_field, conf->rec.mysql_table, conf->rec.username_field, esc_user); } /* perform the query */ if (safe_mysql_query(mysql_res, &result, r, query) == 0){ /* store the query result */ if (result && mysql_num_rows(result) == 1) { sql_row = mysql_fetch_row(result); /* ensure we have a row, and non NULL value */ if (!sql_row || !sql_row[0]) { ARV = AUTH_USER_NOT_FOUND; } else { mysql_password = (char *) apr_pstrcat(r->pool, sql_row[0], NULL); // if (strcmp(mysql_password,password) != 0) { if (strcmp(mysql_password,sha1_password) != 0) { ARV = AUTH_DENIED; } else { ARV = AUTH_GRANTED; } } } } mysql_free_result(result); safe_mysql_rel_server(conf->pool, mysql_res, r); return ARV; }
お分かりだと思いますが、十分なエラー処理など施してませんのでサンプルということで^^;
でビルドを実行すれば、目的のmod_authn_mysql.soが手に入ります。
テストしてみたところ、想定どおりの動きをしてくれています。
思った通り(?!)手間がかかりましたが、目的(id:sikakura:20101124)のことができて良かったです。