善意提醒

如果您打开本站很慢,布局排版混乱,并且看不到图片,那么可能是因为您还没有掌握用科学的方法上网的本领。

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 用户登录的系统上。这样会给用户带来不小的困扰哦,所以最佳的做法应该是判断一下操作系统的版本再去做相应的处理。

没有评论:

发表评论