人は育つのではないが環境の中で変わっていくもの

ふとハテナの人気記事を見てると目を見張るような、おもしろ・・いや、恐ろしいことを見つけました。
いろんな意味でカユイ現象です。


それは、芦屋広太氏の5分で人を育てる技術 の第5回 言うことを聞かない“自信過剰な部下”の記事に対し、
小野和俊氏が芦屋広太氏のマネジメント手法を

奴隷としてこき使っていくためにはどのような小賢しくて汚いテクニック

と揶揄し、それに対して多くの賛同がえられていることです。
奇しくも小野和俊氏と私は、同年代。


その私の経験では、芦屋広太氏のやり方は、集団活動としては一般的で、
当然行われるマネジメントと考えます。
彼はある心理を使用した説得を行っています*1
それは「別チャネルから同質の情報を受け取ると人は真実と受け取ってしまう」心理です。
このケース自身、ハーバードビジネスレビューなどでも幾度となく取り上げられる話題であったりします。*2
これを根回しと簡単に嫌悪するのはいかがなものかと。


たいていの方々が雇われの身で、鼻持ちなら無い後輩がいるのではないでしょうか。
どちらかというと芦屋広太氏の立場の方が多いはず。
小野和俊氏の意見が圧倒的に有利なのはバンドワゴン効果でしょうか。
それとも、ケースの坂本君に同調してしまっているのでしょうか。



そもそも、このような「ケーススタディー」は
完全に理性的な答えを出すには情報が足りないように作られています。
皆さんも登場人物のパーソナリティーすら十分に把握できないでしょう。
結局、自分が今まで養ってきたバックグラウンドをもとに想像して話すしかありません。
だから、ケーススタディー」では意見が違うのは当然です。
ブレーンストーミングやディスカッションでお互いの意見をぶつけ合うのが醍醐味です。
芦屋広太氏と小野和俊氏。当然と主張が違います。


更に、マネジメントはその定義だけでは、結果を出すためのあらゆる手法を禁じていません。
だれもが手を打って賛成できる方法から、人に言えない泥臭い方法まで、それがマネジメントです。
特にこのケースは、組織の心理に関わる部分のため、様々な意見が出てきて当然だと思います。
にもかかわらず、意見が反-芦屋広太氏に固まってしまっている*3
この現象・・・個人的に苦味を感じます。



と言うことで、マジョリティーが決定している段階で意見を言うことはアレかもしれないですが、
せっかくなので踏み込んで自らの意見を述べることとしましょう。




それにしても本件に関するブログ、コメント、TBを読んでビックリします。
まるで、坂本君が一般的な能力、もしくはそれ以上の能力を持つかの如く扱われているのはどういうことでしょうか?
更に、管理職側が無能であるのは、彼の能力を利用できないことだと結論付けています。
それほど彼がすごいと思ったのはどこからでしょうか?
同じ文章を読んだ時、私は坂本君が真性インテリバカではないか?と思ってしまいました。
もともとの文章の書き手が坂本君の印象を悪くするように書いているかもしれませんが、
それを考慮しても、坂本君はインテリバカと思える節があります。


その理由を背理法で説明しましょう。
試しに、会社側に問題があったとします、そんな会社ではもったいない能力を彼が持っているとします。
それなら彼は3年で出て行くでしょう。自分の能力が生かせないことが分からないほど無能では無いのですから。
次に、彼がそこそこ有能だとしましょう。そうすると、6年も働くと業務の中核を担っていてもおかしくないでしょうか。
仮に部長に「彼を下さい」と引き抜きを願ったとしても、「嫌な奴だけど外せない」と断られるでしょう。
では、6年も仕事をしてる有能な彼が、部長から「いつでも(異動して)OK」なんて言われるのはどうしてでしょう。
少なくとも、坂本君は会社に貢献していると思われていないのでしょう。
6年という時間をかけて、自分の価値を認め「させて」いない彼が本当に有能なのでしょうか。
このケースは上司の問題とは思えません。当人の問題ではないかと。


働かざるもの食うべからず。貢献しないもの組織のやり方に物言う資格なし。
どんなに優秀なものであっても最初は発言権が無いのはこれが理由です。
もちろん、新鮮な視点からの進言と言うものも組織には重要でしょう。
これはこれで価値がありますが、多くの組織はCSの観点からお客様からの声を入れることでカバーしようとしています。
社員に対しては入社数年の間で十分でしょう。
それを超えた場合は、職位もしくは実質的な権威を基にした形での発言を望まれます。
彼は、6年間、発言できる基盤を築き上げてきませんでした。
せめて社外での貢献があればよいですが、それも認知されていないのでしょう。
どんなに良い素質があっても、「自分の能力を組織活動に生かすこと」がなければ無能扱いです。


このことは、エンジンが良くても運転が下手なことにたとえてみた方が分かりやすいかもしれません。
会社と言う道路を走ることを求められているのに、
「俺はできるんだ」とエンジンを高らかに噴かされても仕方ないでしょう。
道路は完璧ではありません。どんなに舗装された道路でも小石ぐらいは落ちているでしょう。
ほんどの方は悪路でしょう。だから、小石どころか大石だって転がっていて普通です。
その会社と言うコースで良いラップタイムを出すことを会社に求められているのです。


それにもかかわらず、成績を出していないドライバーが突然、
「このコースは俺のエンジンを生かせないからおかしい。コースを変えろ」
と喚き出しても、だれが聞くのでしょうか?
「お前の走り方を変えろ」と馬鹿にされるのが関の山です。
本当にエンジンに自身があるなら、直線1/4マイルを走らせるドラッグレースにでも参加すれば良いんです。
小さくて代わり映えのしないコースを走ってもつまらないでしょう。
さっさと出てお行きなさい。


そのチャンスも実力も無いのだったら、
少しはドライビングテクニックを学んだほうがましではないかと考えます。


その中で、芦屋広太氏は心理的誘導を用いて彼の行動スタイルを変更させると言う手法を提案しています。
良心的なことだと思います。
管理職側が無能であるのは、彼の能力を利用できないことと主張している方に一言申し上げたい。
坂本君が坂本君のままで有効に利用する方法があることをご存知ですか?


彼の有効利用法。
それは、失敗する案件を彼に押し付け、全ての罪を被ってもらうということです。
鼻持ちなら無いパーソナリティーはそういう運命を引き受ける魅力があります。


それが邪法?


・・・そりゃ、そうでしょうネ



でも、そんなのがマネージメントではまかり通ります。

経験の長いマネージャーはこういった邪法のストックをいくつも持っているでしょう。
それだけで出世していく人もいるでしょう。
かの著名なドラッカーですら、「自分じゃ首を切るのが嫌だから、切り役の人間を側に置いた。」*4とのこと。
言っておきますが、それくらい自分のためにしか生きていない人がいるのが社会です。


スケープゴートに仕立て上げられる体験をした人間にとって見れば、
芦屋広太氏はなんと優しいマネージャーじゃないかと思ってしまうわけで。
皆さんにはそういう経験は無いのでしょうか。



このカユイ現象。
所詮これも、「右に同じ」でいいじゃない。 - edo13thの日記 で述べさせていただいた現象が発生しているのかな。

*1:この説得は専門用語として強制説得にあたりますでしょうか。

*2:古いものですが2003/9/JP版は全般的に本件に関係するでしょう。特集には驚かれるかもしれませんが。

*3:無論、そうじゃない方もいらっしゃいますが

*4:ハーバードビジネスレビュー 2003/11/JP版

sftp:6 あとがき

この問題に取り組もうと思ったのは昨日の昼間。
実際に作業開始できたのは19:00過ぎ。
情報を集めるところから初め、方針を決められたのは翌0:00過ぎ。
03:00に作業終了し、翌日(本日)は自治会に参加。
11:30からBlogに記入を開始、17:30頃投稿。


コードはスピード優先なのでこだわりが無い。
特に文字コードをパラメータ化できなかったのは調査が一番かかったから*1
コードもやり方も初見。
このレベルでも物ができるようしてくれた先達に感謝しなくては。


一番時間がかかったのは下準備。
configure通すまで一体何度 apt-get したことか。


そして、興味に取られる。
phpのmb_strlen見たいなのCに無いかな?」と探しても見つからない。
SJIS, UTF-8, UCS2 のどれであっても strlen でバイナリ長が分かるのだろうか?
 やはり、locationを毎回変えないといけないのか?」という誘惑になんとか耐える。


最終的に悲しかったのは、パッチとして提供できなかったこと。
Makefileで glib を入れ込むには configure を知らないとなぁ・・・
あれの解析で十分精神が浪費されるので、さらにがんばろうと言う気にならないなぁ

2006.12.10
・・・・追記しときます。
チェンジルートはopendir()のみしか対応していないので、変なパスに直接ファイルを作成するとかは可能です。
時間があったら直します。

*1:それ以上に文章を作るのに時間がかかっているが

sftp:5 解決編その2 SFTP の文字化け問題解決

最終章はソースコードを直接いじり、文字化け問題を解決しよう。


その前に、使用する環境のプロファイルを説明しよう。


使用環境


Debian を用いているため、apt を用いて sshのソースを取得する。

apt-get source ssh


そして、sftp-server.c を以下のように修正。

  #endif
+  
+  #include "sftp_iconv.c"
  
  /* input and output queue */
  Buffer iqueue;
  Buffer oqueue;

sftp_iconv.c を作成する。

#include	<stdio.h>
#include	<stdlib.h>
#include	<string.h>
#include	<iconv.h>
#include	<errno.h>
#include	<dirent.h>
#include	<unistd.h>
#include	<glib.h>

//#define SFTP_ICONV_DEBUG 1
#define SFTP_HIDE_PARENT_DIR 1


#ifdef SFTP_ICONV_DEBUG
#	define DEBUG_FILE	"/tmp/sftp-out.txt"

	void sftp_print(const char* string,const char* printpath);
	void sftp_printInt(const char* string, int i);
	void sftp_putcs(const char* string, const char* printpath,int length);
#else  //SFTP_ICONV_DEBUG
#	define sftp_print(a,b)
#	define sftp_printInt(a,b)
#	define sftp_putcs(a,b,c)
#endif //SFTP_ICONV_DEBUG

#ifdef SFTP_HIDE_PARENT_DIR
	const static char dum_dir=0x00;
	int isUnderHome(const char* path);
#else  //SFTP_HIDE_PARENT_DIR
#	define isUnderHome(a) 1
#endif //SFTP_HIDE_PARENT_DIR



#define SFTP_CLIENT_CHARSET	"CP932"
#define SFTP_SERVER_CHARSET	"UTF-8"

int client_pathLen(const char* client_path){
// SFTP_CLIENT_CHARSET == "CP932"
	const unsigned char* p;
	int r=0;
	if(!client_path)return 0;
	for(p=(const char*)client_path;*p;p++)r++;
	return r;
// #error
}
int server_pathLen(const char* server_path){
// SFTP_SERVER_CHARSET == "UTF-8"
	const unsigned char* p;
	int r=0;
	if(!server_path)return 0;
	for(p=(const char*)server_path;*p;p++)r++;
	return r;
//#error
}


int _conv_open(const char* client_path, int flags, mode_t mode)
{
	int  r;
	char* server_path;

sftp_putcs("[open:start]",client_path,client_pathLen (client_path));
	server_path = g_convert (client_path, client_pathLen (client_path),
		  SFTP_SERVER_CHARSET, SFTP_CLIENT_CHARSET,  NULL, NULL, NULL); 
	if(!server_path) { errno = ENOMEM; return -1; }

	r = open(server_path, flags, mode);
sftp_print("[open:end] %s",server_path);
	g_free(server_path);

	return r;
}


int _conv_creat(const char* client_path, mode_t mode)
{
	int		r;
	char* server_path;

sftp_putcs("[creat:start]",client_path,client_pathLen (client_path));
	server_path = g_convert (client_path, client_pathLen (client_path),
		  SFTP_SERVER_CHARSET, SFTP_CLIENT_CHARSET,  NULL, NULL, NULL); 
	if(!server_path) { errno = ENOMEM; return -1; }

	r = creat(server_path, mode);
sftp_print("[creat:end] %s",server_path);
	g_free(server_path);

	return r;
}


int _conv_mkdir(const char* client_path, mode_t mode)
{
	int		r;
	char* server_path;

sftp_putcs("[mkdir:start]",client_path,client_pathLen (client_path));
	server_path = g_convert (client_path, client_pathLen (client_path),
		  SFTP_SERVER_CHARSET, SFTP_CLIENT_CHARSET,  NULL, NULL, NULL); 
	if(!server_path) { errno = ENOMEM; return -1; }

	r = mkdir(server_path, mode);
sftp_print("[mkdir:end] %s",server_path);
	g_free(server_path);

	return r;
}


int _conv_rmdir(const char* client_path)
{
	int		r;
	char* server_path;

sftp_putcs("[rmdir:start]",client_path,client_pathLen (client_path));
	server_path = g_convert (client_path, client_pathLen (client_path),
		  SFTP_SERVER_CHARSET, SFTP_CLIENT_CHARSET,  NULL, NULL, NULL); 
	if(!server_path) { errno = ENOMEM; return -1; }

	r = rmdir(server_path);
sftp_print("[rmdir:end] %s",server_path);
	g_free(server_path);

	return r;
}


int _conv_unlink(const char* client_path)
{
	int		r;
	char* server_path;

sftp_putcs("[ulink:start]",client_path,client_pathLen (client_path));
	server_path = g_convert (client_path, client_pathLen (client_path),
		  SFTP_SERVER_CHARSET, SFTP_CLIENT_CHARSET,  NULL, NULL, NULL); 
	if(!server_path) { errno = ENOMEM; return -1; }

	r = unlink(server_path);
sftp_print("[ulink:end] %s",server_path);
	g_free(server_path);

	return r;
}


int _conv_remove(const char* client_path)
{
	int		r;
	char* server_path;

sftp_putcs("[remove:start]",client_path,client_pathLen (client_path));
	server_path = g_convert (client_path, client_pathLen (client_path),
		  SFTP_SERVER_CHARSET, SFTP_CLIENT_CHARSET,  NULL, NULL, NULL); 
	if(!server_path) { errno = ENOMEM; return -1; }

	r = remove(server_path);
sftp_print("[remove:end] %s",server_path);
	g_free(server_path);

	return r;
}


int _conv_rename(const char* client_path_old, const char* client_path_new)
{
	int		r;
	char* server_path_old, server_path_new;

sftp_putcs("[rename:start:old]",client_path_old,client_pathLen (client_path_old));
sftp_putcs("[rename:start:new]",client_path_new,client_pathLen (client_path_new));
	
	server_path_old = g_convert (client_path_old, client_pathLen (client_path_old),
		  SFTP_SERVER_CHARSET, SFTP_CLIENT_CHARSET,  NULL, NULL, NULL); 
	if(!server_path_old) { errno = ENOMEM; return -1; }

	server_path_new = (char*)g_convert ((const gchar *)client_path_new, 
		client_pathLen (client_path_new), SFTP_SERVER_CHARSET, SFTP_CLIENT_CHARSET,
		 NULL, NULL, NULL); 
	if(!server_path_new) { g_free(server_path_old);errno = ENOMEM; return -1; }

	r = rename((const char*)server_path_old, (const char*)server_path_new);
sftp_print("[rename:end:old] %s",server_path_old);
sftp_print("[rename:end;new] %s",server_path_new);
	g_free(server_path_old);
	g_free((gchar*)server_path_new);

	return r;
}


#ifdef	_USE_FILE_OFFSET64
int _conv_stat(const char* client_path, struct stat64* buf)
#else
int _conv_stat(const char* client_path, struct stat* buf)
#endif
{
	int  r;
	char* server_path;

sftp_putcs("[stat:start]",client_path,client_pathLen (client_path));
	server_path = g_convert (client_path, client_pathLen (client_path),
		  SFTP_SERVER_CHARSET, SFTP_CLIENT_CHARSET,  NULL, NULL, NULL); 
	if(!server_path) { errno = ENOMEM; return -1; }
	r = stat(server_path, buf);
sftp_print("[stat:end] %s",server_path);
	g_free(server_path);

	return r;
}


#ifdef	_USE_FILE_OFFSET64
int _conv_lstat(const char* client_path, struct stat64* buf)
#else
int _conv_lstat(const char* client_path, struct stat* buf)
#endif
{
	int		r;
	char* server_path;

sftp_putcs("[lstat:start]",client_path,client_pathLen (client_path));
	server_path = g_convert (client_path, client_pathLen (client_path),
		  SFTP_SERVER_CHARSET, SFTP_CLIENT_CHARSET,  NULL, NULL, NULL); 
	if(!server_path) { errno = ENOMEM; return -1; }

	r = lstat(server_path, buf);
sftp_print("[lstat:end] %s",server_path);
	g_free(server_path);

	return r;
}



DIR* _conv_opendir(const char* client_path)
{
	DIR*	dir;
	char* server_path;
	
sftp_putcs("[opendir:start]",client_path,client_pathLen (client_path));

	server_path = g_convert (client_path, client_pathLen (client_path),
		  SFTP_SERVER_CHARSET, SFTP_CLIENT_CHARSET,  NULL, NULL, NULL); 
	if(!(int)server_path) { errno = ENOMEM; return -1; }

sftp_printInt("[isUnderHome:ret]%d",isUnderHome(server_path));
	if(!isUnderHome((const char*)server_path)){ g_free(server_path); return &dum_dir; }

	dir = opendir(server_path);
sftp_print("[opendir:end] %s",server_path);	
	g_free(server_path);

	return dir;
}


#ifdef	_USE_FILE_OFFSET64
struct dirent64* _conv_readdir(DIR* dir)
#else
struct dirent* _conv_readdir(DIR* dir)
#endif
{
	static struct dirent*	d = NULL;
	struct dirent*	r;

	if(dir==(DIR*)&dum_dir) return NULL;
	
	r = readdir(dir);
	if (r == NULL) return NULL;

	if (d) free(d);
	d = malloc(sizeof(struct dirent)+server_pathLen(r->d_name)+1);
	if (!d) { errno = ENOMEM; return NULL; }

	memcpy(d, r, sizeof(struct dirent));

sftp_print("[readdir:start] %s",r->d_name);
	int server_path_len=server_pathLen( r->d_name );
	char* client_path;
	client_path = g_convert (&(r->d_name[0]), server_path_len,
		 SFTP_CLIENT_CHARSET, SFTP_SERVER_CHARSET,  NULL, NULL, NULL); 
	if(!client_path) {/* free(d) ;*/ errno = ENOMEM; return NULL; }
sftp_putcs("[readdir:end]",client_path,client_pathLen (client_path));
	memcpy(&(d->d_name[0]),client_path,server_path_len+1);
	g_free(client_path);

	return d;
}

#ifdef	_USE_FILE_OFFSET64
int _conv_closedir(DIR* dir)
#else
int _conv_closedir(DIR* dir)
#endif
{
	if(dir==(DIR*)&dum_dir)return 0;
	return closedir(dir);
}

char* _conv_realpath(char* client_path, char resolved_path[])
{
	char*	r;
	char*	server_path;
	char*	server_resolved = malloc(MAXPATHLEN+1);
	if(!(int)server_resolved) { errno = ENOMEM; return -1; }

sftp_putcs("[realpath:start]",client_path,client_pathLen (client_path));
	server_path = g_convert (client_path, client_pathLen (client_path),
		  SFTP_SERVER_CHARSET, SFTP_CLIENT_CHARSET,  NULL, NULL, NULL);
	if(!server_path) { free(server_resolved);errno = ENOMEM; return -1; }

	r = realpath(server_path, server_resolved);
	if (r)
	{
sftp_print("[realpath:mid] %s",server_resolved);
		int server_resolved_len=server_pathLen( server_resolved );
		char* client_path2;
		client_path2 = g_convert (server_resolved, server_resolved_len,
			 SFTP_CLIENT_CHARSET, SFTP_SERVER_CHARSET,  NULL, NULL, NULL); 
		if(!client_path2) {
			g_free(server_path);
			free(server_resolved);
			errno = ENOMEM;
			resolved_path[0] = NULL;
			return 0;
		}
sftp_putcs("[realpath:end]",client_path2,client_pathLen (client_path2));
		memcpy(resolved_path,client_path2,client_pathLen(client_path2)+1);
		g_free(client_path2);
	} else {
		resolved_path[0] = NULL;
	}
	g_free(server_path);
	free(server_resolved);

	return r;
}


int _conv_readlink(const char* client_path, char* buf, size_t bufsiz)
{
	int		r;
	char* server_path;

sftp_putcs("[readlink:start]",client_path,client_pathLen (client_path));
	server_path = g_convert (client_path, client_pathLen (client_path),
		  SFTP_SERVER_CHARSET, SFTP_CLIENT_CHARSET,  NULL, NULL, NULL); 
	if(!server_path) { errno = ENOMEM; return -1; }

	r = readlink(server_path, buf, bufsiz);
sftp_print("[readlink:end] %s",server_path);
	g_free(server_path);

	return r;
}


int _conv_symlink(const char* client_path_old, const char* client_path_new)
{
	int		r;
	char* server_path_old, server_path_new;

sftp_putcs("[symlink:start:old]",client_path_old,client_pathLen (client_path_old));
sftp_putcs("[symlink:start:new]",client_path_new,client_pathLen (client_path_new));

	server_path_old = g_convert (client_path_old, client_pathLen (client_path_old),
		  SFTP_SERVER_CHARSET, SFTP_CLIENT_CHARSET,  NULL, NULL, NULL); 
	if(!server_path_old) { errno = ENOMEM; return -1; }

	server_path_new = g_convert (client_path_new, client_pathLen (client_path_new),
		  SFTP_SERVER_CHARSET, SFTP_CLIENT_CHARSET,  NULL, NULL, NULL); 
	if(!server_path_new) { g_free(server_path_old); errno = ENOMEM; return -1; }

	r = symlink(server_path_old, server_path_new);
sftp_print("[symlink:end:old] %s",server_path_old);
sftp_print("[symlink:end:new] %s",server_path_new);
	g_free(server_path_old);
	g_free(server_path_new);

	return r;
}


int _conv_chmod(const char* client_path, mode_t mode)
{
	int		r;
	char* server_path;

sftp_putcs("[chmod:start]",client_path,client_pathLen (client_path));
	server_path = g_convert (client_path, client_pathLen (client_path),
		  SFTP_SERVER_CHARSET, SFTP_CLIENT_CHARSET,  NULL, NULL, NULL); 
	if(!server_path) { errno = ENOMEM; return -1; }

	r = chmod(server_path, mode);
sftp_print("[chmod:end] %s",server_path);
	g_free(server_path);

	return r;
}


int _conv_chown(const char* client_path, uid_t owner, gid_t group)
{
	int		r;
	char* server_path;

sftp_putcs("[chown:start]",client_path,client_pathLen (client_path));
	server_path = g_convert (client_path, client_pathLen (client_path),
		  SFTP_SERVER_CHARSET, SFTP_CLIENT_CHARSET,  NULL, NULL, NULL); 
	if(!server_path) { errno = ENOMEM; return -1; }

	r = chown(server_path, owner, group);
sftp_print("[chown:end] %s",server_path);
	g_free(server_path);

	return r;
}


int _conv_lchown(const char* client_path, uid_t owner, gid_t group)
{
	int		r;
	char* server_path;

sftp_putcs("[lchown:start]",client_path,client_pathLen (client_path));
	server_path = g_convert (client_path, client_pathLen (client_path),
		  SFTP_SERVER_CHARSET, SFTP_CLIENT_CHARSET,  NULL, NULL, NULL); 
	if(!server_path) { errno = ENOMEM; return -1; }

	r = lchown(server_path, owner, group);
sftp_print("[lchown:end] %s",server_path);
	g_free(server_path);

	return r;
}


int _conv_utime(const char* client_path, struct utimbuf* buf)
{
	int		r;
	char* server_path;

sftp_putcs("[utime:start]",client_path,client_pathLen (client_path));
	server_path = g_convert (client_path, client_pathLen (client_path),
		  SFTP_SERVER_CHARSET, SFTP_CLIENT_CHARSET,  NULL, NULL, NULL); 
	if(!server_path) { errno = ENOMEM; return -1; }

	r = utime(server_path, buf);
sftp_print("[utime:end] %s",server_path);
	g_free(server_path);

	return r;
}


int _conv_utimes(const char* client_path, struct timeval* tvp)
{
	int		r;
	char* server_path;

sftp_putcs("[utimes:start]",client_path,client_pathLen (client_path));
	server_path = g_convert (client_path, client_pathLen (client_path),
		  SFTP_SERVER_CHARSET, SFTP_CLIENT_CHARSET,  NULL, NULL, NULL); 
	if(!server_path) { errno = ENOMEM; return -1; }

	r = utimes(server_path, tvp);
sftp_print("[utimes:end] %s",server_path);
	g_free(server_path);

	return r;
}



#ifdef	open
#undef	open
#endif
#ifdef	creat
#undef	creat
#endif

#ifdef	stat
#undef	stat
#endif

#ifdef	lstat
#undef	lstat
#endif

#ifdef	readdir
#undef	readdir
#endif

#define	open		_conv_open
#define	creat		_conv_creat
#define	mkdir		_conv_mkdir
#define	rmdir		_conv_rmdir
#define	unlink		_conv_unlink
#define	rename		_conv_rename
#define	remove(a)		_conv_remove(a)
#define	stat(X, Y)	_conv_stat(X, Y)
#define	lstat(a, b)		_conv_lstat(a, b)
#define	opendir(a)		_conv_opendir(a)
#define	readdir(a)		_conv_readdir(a)
#define	closedir(a)		_conv_closedir(a)
#define	realpath(a, b)	_conv_realpath(a, b)
#define	readlink(a, b, c)	_conv_readlink(a, b, c)
#define	symlink(a, b)		_conv_symlink(a, b)
#define	chmod(a, b)		_conv_chmod(a, b)
#define	chown(a, b, c)		_conv_chown(a, b, c)
#define	lchown(a, b, c)		_conv_lchown(a, b, c)
#define	utime(a, b)		_conv_utime(a, b)
#define	utimes(a, b)		_conv_utimes(a, b)


#ifdef SFTP_ICONV_DEBUG
void sftp_print(const char* string,const char* printpath){
	FILE *sftp_file;
	sftp_file=fopen(DEBUG_FILE,"a+");
	if(!sftp_file)return;

	fprintf(sftp_file,string,printpath);
	fprintf(sftp_file,"\n");
	fclose(sftp_file);
	return;
}
void sftp_printInt(const char* string, int i){
	FILE *sftp_file;

	sftp_file=fopen(DEBUG_FILE,"a+");
	if(!sftp_file)return;

	fprintf(sftp_file,string,i);
	fprintf(sftp_file,"\n");
	fclose(sftp_file);
	return;
}
void sftp_putcs(const char* string, const char* printpath,int length){
	FILE *sftp_file;
	int i;
	sftp_file=fopen(DEBUG_FILE,"a+");
	if(!sftp_file)return;

	fprintf(sftp_file,string);
	for(i=0;i<length;i++) fputc(*printpath++,sftp_file);
	fprintf(sftp_file,"\n");
	fclose(sftp_file);
	return;
}
#endif //SFTP_ICONV_DEBUG

#ifdef SFTP_HIDE_PARENT_DIR
int isUnderHome(const char* path){
	char *home= g_get_home_dir();
	char *p=path;
	int i,j;
	if(!home)return 0;

	sftp_print("[isUnderHome]%s",home);
	i=strlen(home);
	j=strlen(p);
	
	if(home[i-2]=='/')i--;
	if(p[j-2]=='/')j--;
	if(j<i)return 0;
	
	if(memcmp(home,p,i))return 0;
	
	if(p[i]==0)return 1;
	if(p[i]=='/')return 1;
	
	return 0;
}
#endif //SFTP_HIDE_PARENT_DIR

そのあと ./configure し、できた Makefile に glib が利用できるようにする。

 122 .c.o:
 123         $(CC) $(CFLAGS) $(CPPFLAGS) -c $<
 124
+125 sftp-server.cftp-server.o:       sftp-server.c
+126         $(CC) $(CFLAGS) -I/usr/include/glib-2.0 -I/usr/lib/glib-2.0/include $(CPPFLAGS) -c $<
 127 
 128 LIBCOMPAT=openbsd-compat/libopenbsd-compat.a

 161 sftp-server$(EXEEXT): $(LIBCOMPAT) libssh.a sftp.o sftp-common.o sftp-server.o
-162        $(LD) -o $@ sftp-server.o sftp-common.o $(LDFLAGS) -lssh -lopenbsd-compat $(LIBS) /usr/lib/libglib-2.0.a
+162        $(LD) -o $@ sftp-server.o sftp-common.o $(LDFLAGS) -lssh -lopenbsd-compat $(LIBS)
 163

後は make し、出来上がった sftp-server モジュールを /usr/libへ持っていく

 # mv sftp-server sftp-server-new
 # mv sftp-server-new /usr/lib

最期は、 sshd_config を修正し、新しく作成した sftp-server モジュールを利用するようにする。

- Subsystem	sftp	/usr/lib/sftp-server
+ Subsystem	sftp	/usr/lib/sftp-server-new


あとは、FileZillaWinSCP からアクセスし、保存したファイルをサーバーにログインしても見られることを確認できれば成功。

sftp:4 解決編その1 SFTP の文字化け問題解決の方針

前章で述べたとおり、Windows から FileZillaWinSCP といった SFTPクライアント を使ってファイルを転送すると、
ファイル名は SJIS で送られてくる。SFTPサーバー はその文字をサーバーで使用している文字コードに変換することなく書き込む。


そのため、基本的な考え方はいたってシンプルである。
SFTPクライアント から書き込まれた SJIS をサーバーの文字コード UTF-8 に変換し、
SFTPクライアントにファイル名を送るときは逆に UTF-8 から SJIS に変換するだけである。


実際、T.Tsujikawa氏はとっくの昔にパッチを作成している。
しかし、サーバーの文字コードEUC の場合のものなので、今回作りたい UTF-8 のものとは異なる。
そこで彼のソースコードをベースにして、glib の文字コード変換ルーチン(g_convert)を用いる。
例えば、SJIS から UTF-8 に変換する場合は下記のようにする。

utf8 = g_convert (string, strlen (string), "UTF-8", "CP932",  NULL, NULL, NULL);


CP932 は MicrosoftSJIS のことだと考えてもらってかまわない。





そして、ついでと言ってはなんだが、チェンジルートの仕組みも作る。
チェンジルートは NADMIN氏が作成したパッチを用いることでも可能である。
このパッチは正規版にも取り込まれており、オプションで有効になるようであるが、openssh-portable の変更を必要とするなどライブラリの影響範囲が大きい。
同じく、外部的にチェンジルートを可能にする chroot jail も SSH そのものを防いでしまう形となり、SFTP のみを防ぐことができない。
この結果、既に SSH をコンソール等で利用している環境では用いることが難しい。


今回は、SFTP のみをいじるだけでチェンジルートするようにし、
運用的な面でも、ソースコードの面でも影響範囲を小さくすることが目的である。


チェンジルートの仕組みも間単である。自分のホームディレクトリより上位のディレクトリの操作を常に失敗すればよい。
もちろん。これは完全なチェンジルートではなく、擬似的なものである。
だが十分に役立つだろう。


次章では、上記二つの機能を持ったソース sftp_iconv.c をつくり、 sftp-server.c から利用することで実装する。

sftp:3 SFTPサーバー の貧相さ

Linux上で SFTPサーバー は sshサーバー にアドオンとして搭載される。
そのため、FTPサーバー としてメジャーに使われている wu-ftpd*1proftpd を用いることができない。
FTPサーバー で求められている機能は、大きく分けて二つある。
一つは、他の仕組みと独立してユーザー管理できること。
そして、見せたくないフォルダをみせずに済むチェンジルートの仕組みである。
SFTPサーバー はこれら2つを持っていないのだが、それがどれだけ悲惨なことか説明しよう。


一般的にユーザー管理の方法は、サーバーマシン自身での管理とサービスを行うアプリケーション毎の管理に分けられる。
ここでサーバーマシンと同じ管理手法を行ってしまうと、SFTPサーバー を使う人は他のサービスのアプリケーションを使うことが可能になってしまう。
一旦サーバーと同じ管理とし、pam/SELinuxLDAP 、passwdとシェルを組み合わせることでセキュリティーを強化することも不可能ではない*2
が、これらの設定を行うには幅広い知識が必要でサーバー管理者のスキルが問われる。そのため躊躇しても仕方が無いだろう。
一方、サービスを行うアプリケーション毎の管理であれば、非常に簡単に設定が行える。


チェンジルートの仕組みも同様に重要である。なぜならば、何も設定していない FTPサービス は重要なデータを取得することが可能である。
ユーザーを管理している passwdファイル を取得することや、各システムファイルも閲覧が可能となる。
これらのファイルは一般的なユーザーには必要の無いものであろう。
チェンジルートは不必要なフォルダに入ることを防ぐ。結果、他のディレクトリにある重要な情報にアクセスできなくなる。
従ってチェンジルートでセキュリティを強化することができる。


このように、アプリケーションで独立したユーザー管理、チェンジルートの仕組み、両方ともセキュリティーに関わる問題である。
しかし、SFTPサーバー はほとんどのサーバー管理者が当然と思っている機能すらデフォルトで持っていない*3


その上、これら問題すら霞んでしまう問題がある。
それはファイル名が文字化けしてしまうことである。


例えば、Windowsの日本語のファイルを SFTP 経由で遠隔地のサーバーに入れるとしよう。
この日本語ファイルはサーバーを直接見ようとしても文字化けをしてしまっている。
遠隔地の Windowsマシン から Sambaサービス 経由で見ても同様に見られない。
これは、サーバーの内部で用いている文字コードUTF-8 であるにもかかわらず、
Windows で使用している SFTPクライアント から送られてくる文字コードSJIS で、
SFTPサーバー がその SJIS の文字を無理やり保存していることから生じている。


そのため SFTP 経由であればファイル名は取得できる。けれど他のサービスと兼用できないのは大きくマイナスだろう。
これらの理由から、SFTP を使用せずに転送経路をとにかく暗号化する IPSec を使い、その上で FTP を行う試みが多いのも納得できる。

*1:最近では非推奨が多いですが・・・

*2:TCPラッパーやxinetは割愛

*3:後述 NADMIN氏

sftp:2 最初に FTP と暗号化

FTP はファイルを転送するための通信の仕組み。メールやウェブほどではないが、かなりメジャーなものだろう。
特徴は、大きなサイズのデータが送受信できること。
メールやウェブだと大体2メガぐらいのサイズのものしか一度に送れないが、FTP は数ギガのファイルも送信が可能だ。
少なくない人が FFFTPSmartFTP を使ってダウンロードを体験したことがあるのじゃないだろうか。


しかし、この FTP には一つだけ、問題がある。それは暗号化がそれほど盛んでないこと。
暗号化といえば、ウェブだと HTTPS。メールだと PGP*1 があり、
仕事だけでなく、一般的にも使われるようなってきている。
これらは送信者と受信者の間はデータを見えなくすることでデータを安全にやり取りすることが可能となる。


これではだめだと、FTPを暗号化したものも作られている。
それは SFTP というもの*2。これは、SSHという転送経路を暗号化する仕組みを利用する。
このSSHの中でファイル転送を行うと、第三者からはデータを見ることができなくなる。
実際にこの SFTP を使用できる無料のアプリケーションとして FileZillaWinSCP が用意されている。
ならなぜ 暗号化が盛んでないのだろうか?その理由として挙げられるのが、SFTPサーバー の機能が貧相だからである。

*1:正確には経路の暗号化じゃないですけどね

*2:文脈上 SCP の説明を避ける

sftp:1 windowsクライアントから使えるSFTPサーバー

超久しぶりなんだけど、今回もやっぱりマニアックなネタを提供してしまう。
今回は 暗号化された FTPサービス 「SFTP」 が抱える、文字化け問題の解決だ。


概要についてはできるだけ多くの人に理解してもらえるように書いているつもりだ*1
そんなの分かっているよと言う方は解決編(その1その2)から読んでもらいたい。
逆に、概要だけ分かればいいよという人は、解決編が分からなければ読み飛ばしてもらってかまわない。

*1:これを読もうと言う一見さんはいないだろうけれどスタイルとしてね