2008-04-29

在Windows服务中以SYSDBA无需密码登录本地Oracle

因为客户的特殊需求,导致我们要在一个Windows服务中以SYSDBA权限去访问本地的Oracle数据库,并且不能输入任何密码。通常情况下,当Windows的用户帐号是ORA_DBA组成员时,直接用sqlplus "/ as sysdba"的方式就可以登录本地Oracle数据库,无需输入sys的用户名和密码。因此我计划用这种方式来实现无密码登录Oracle。但是得到了如下的错误信息:
ORA-01031: insufficient privileges
很明显,权限方面出了问题。想想也应该,我们的Windows服务使用的应该是NT AUTHORITY/SYSTEM(S-1-5-18)的权限在运行。于是方案A出台了。

方案A:将SYSTEM帐户加入ORA_DBA组。

从UI是看不到SYSTEM帐户的,只有用命令行的方式加。
net localgroup ORA_DBA system /add
结果还是得到了ORA-01031。分析了一下,有可能是因为操作系统认证需要从操作系统的用户帐号来进行,而SYSTEM帐号是个内置帐号,在用户管理里面是看不到的。于是这个方案宣告失败。

方案B:切换到别的帐户运行。
基本上,方案A走不通的话,那就只有这种办法了。首先是建立一个Windows帐户,并加入ORA_DBA组中。密码自己随便定一个就好。
net user dbauser 1qaz2wsx /add
net localgroup ORA_DBA dbauser /add
但是具体怎么切换呢?

方案B.1:用runas切换帐户。
一般RunAs服务(也就是Secondary Logon)都是启动的,所以首先想到用Windows 2000/XP/2003自带的命令runas来实现。
runas /user:dbauser "sqlplus \"/ as sysdba\" ……"
没报错,可是没反应。噢,麻烦来了。这个runas命令是要问密码的。
上Google找了一下解决方案。试了一下用vbs的SendKeys那种,手动状态下似乎勉强可行,但要放在Windows服务中,没戏。因为它毕竟是模拟客户端的输入来键入密码的。有个自称可以让runas支持管道输入密码的小工具sanur也没戏,看起来它俩的原理是类似的。

方案B.2:用第三方工具替代runas切换帐户。
既然runas这条路走不通,就彻底抛开它另寻出路了。
有个工具lsrunas自称可以替代runas,于是下载下来一试。虽然参数比较罗嗦,但的确是可以替代。不过这个替代也仅限于通常情况下,一旦放到我们的Windows服务中,就没有反应了。
另外有人推荐了一个cpau。这个工具还是不行,不过它倒是留下了一个提示信息,这个信息成了重要的线索:
ERROR: CPAU doesn't support running from LocalSystem.
看来还是SYSTEM的权限问题。

方案B.3:自写工具切换帐户。
看来这些工具都没问题,但是应该都不支持在SYSTEM的权限下面切换用户。听说以前某个同事也为这个问题研究了好些天,最后还是放弃了。老实说,听到这个我有一点绝望。
这SYSTEM权限吧,说它低,却能操作很多系统级别的东西。可要说它高呢,有些本来很容易的事情它又做不了。不过理论上还是认为SYSTEM的权限比较“高”,很多黑客都以获得SYSTEM权限为目标在半肉鸡上奋斗着。于是,我以SYSTEM降权为主要话题,去黑客站点看了看。
还真是有收获,得到了一段代码,是一个利用API自己实现的runas工具程序。在命令行下使用了一下,感觉挺好,比Windows那个runas好用多了。
不过,放进Windows服务里面,还是不行。真的是快绝望了。

方案B.4:用API直接切换帐户。
由于我们的系统有现成的接口用来启动一个PipeCmd,所以到目前为止我都是用它去运行了一个批处理,然后在批处理中干上述事情。该接口其实是用CreateProcess实现,因此新建的进程就继承了父进程的权限,也就是SYSTEM。
但,在那段黑客代码中,其实切换帐户是使用了CreateProcessWithLogonW来完成。估计各个工具都类似,因此应该可以跳过中间的多余步骤,用API切换帐户来直接运行sqlplus。
经过多次实验,总算是成功了:
HANDLE hToken;
LogonUser("dbauser", NULL, "1qaz2wsx", LOGON32_LOGON_INTERACTIVE, LOGON32_PROVIDER_DEFAULT, &hToken);
STARTUPINFO si;
si.cb = sizeof(STARTUPINFO);
GetStartupInfo(&si);
PROCESS_INFORMATION pi;
CreateProcessAsUser(hToken, NULL, cmdbuf, &sa, NULL, TRUE, NULL, NULL, NULL, &si, &pi);
想偷懒的人还是看看代码稍微理解一下吧,因为我也想偷一下懒,直接Copy/Paste可能会有问题的。
另外说明一下,CreateProcessWithLogonW应该是也可以做到,但是我一直没能成功。倒是CreateProcessAsUser一次成功,于是就这样用下去了。

本来在这里就应该作结了,不过后面还有一点小插曲:这段代码在Windows 2000/XP上可以运行得很好,但是在2003上有问题。在2003上用这种方式运行sqlplus的时候看起来一切正常,但sql的内容没有被执行。最终的解决方案是把dbauser也给加到了Administrators组里面。
net localgroup Administrators dbauser /add
但是在XP下面要这样做的话,就有可能会让Windows把dbauser当成了主管理员,特别是在某些装好之后默认用Administrator用户登录的系统上。这样会给用户带来不小的困扰哦,所以最佳的做法应该是判断一下操作系统的版本再做相应处理。

没有评论:

发表评论