第九章
9-1 在下列每种情况中,假设进程用户ID的初始值分别是real(实际)=1000、effective(有效)=0、saved(保存)=0、file-system(文件系统)=0。当执行这些调用后,用户ID的状态如何?
- a) setuid(2000);
- b) setreuid(-1, 2000);
- c) seteuid(2000);
- d) setfsuid(2000);
- e) setresuid(-1, 2000, 3000);
我们总结一下课本上有关这几个函数的知识点:
set是函数的开头,后面r是真实id,e是有效id,s是保存id,f是文件id,uid是进程凭证。
对于特权级进程,也就是有效id是0的id,按照函数名可以修改对应标志位到任意值。
对于非特权级进程,有效id不是0,情况比较复杂。
- setuid、seteuid只能修改有效ID
- setresuid能修改所有ID
- setreuid能修改实际id和有效id,但是对实际id不能修改为保存id。
额外的,setreuid无论在哪种情况下,当设置了实际id,或有效id发生变化,保存id将等于有效id。
可以看出setreuid情况是最复杂的,最好不要用这种函数。但是在其他Unix系统中,不提供这个函数。
原来的状态:
real | effective | saved | file-system |
---|---|---|---|
1000 | 0 | 0 | 0 |
a 中尝试修改用户id为2000,有效是特权0情况下,修改所有id,并成功。同时文件id也跟着变化,现在是:
real | effective | saved | file-system |
---|---|---|---|
2000 | 2000 | 2000 | 2000 |
b 中尝试修改有效用户id为2000,成功,同时文件id变化为2000,现在是:
real | effective | saved | file-system |
---|---|---|---|
1000 | 2000 | 0 | 2000 |
c 中尝试修改有效用户id为2000,并触发了saved修改条件,有效id和保存id都改为2000。
real | effective | saved | file-system |
---|---|---|---|
1000 | 2000 | 2000 | 2000 |
d 尝试修改文件系统id,有效。
real | effective | saved | file-system |
---|---|---|---|
1000 | 0 | 0 | 2000 |
e 尝试修改有效id和保存id,有效。
real | effective | saved | file-system |
---|---|---|---|
1000 | 2000 | 3000 | 0 |
9-2 拥有如下用户ID 的进程享有特权吗?
real = 0 effective = 1000 saved=1000 file-system=1000
因为特权只和effective位置有关,所以没有特权。
9-3 使用setgroups()与库函数从密码文件、组文件中获取信息,以实现initgroups()。请注意,欲调用setgroups,必须有特权才可以。
因为c++可以进行更方便的字符串操作,我们使用c++来实现。
#include <unistd.h>
#include <grp.h>
#include <stdio.h>
#include <pwd.h>
#include <fstream>
#include <iostream>
#include <vector>
#include <stdlib.h>
using namespace std;
void SplitString(const std::string &s, std::vector<std::string> &v, const std::string &c)
{
std::string::size_type pos1, pos2;
pos2 = s.find(c);
pos1 = 0;
while (std::string::npos != pos2)
{
v.push_back(s.substr(pos1, pos2 - pos1));
pos1 = pos2 + c.size();
pos2 = s.find(c, pos1);
}
if (pos1 != s.length())
v.push_back(s.substr(pos1));
}
void my_initgroups(string name, int id)
{
// initgroups()用来从组文件(/etc/group)中依次读取数据
// 若该组数据的成员中有参数user 时,便将参数group组识别码加入到该进程中
// 然后额外加入id
// 需要有效权限为0
string buffer;
ifstream in("/etc/group");
vector<int> need_group;
while (!in.eof())
{
getline(in, buffer);
vector<string> list;
SplitString(buffer, list, ":");
if (list.size() > 0)
{
if (list.at(list.size() - 1) == name)
{
need_group.push_back(atoi((list.at(list.size() - 2)).c_str()));
}
}
}
gid_t grouplist[64];
int len = need_group.size();
int i;
for (i = 0; i < len; i++)
{
grouplist[i] = need_group[i];
}
grouplist[i] = id;
setgroups(len + 1, grouplist);
}
int main(void)
{
gid_t grouplist[64];
my_initgroups("pulse", 1234);
getgroups(getgroups(0, grouplist), grouplist);
for (int i = 0; i < getgroups(0, grouplist); i++)
{
printf("%d ", grouplist[i]);
}
return 0;
}
initgroups函数的作用是将给定的name在/etc/group文件中找到所有的父组,然后将父组的id都记下来,最后加上一个额外的id,替换为当前的组id。我们也是这样来实现的。为了方便拆分字符串,使用SplitString来帮助拆分。运行结果如下:
$ g++ 9-2.cpp
$ sudo ./a.out
29 1234
9-4 当前用户凭证如下:
real = X effective = Y saved = Y
如何执行如下操作?
a)挂起和恢复set-user-ID身份
b)永久放弃set-user-ID身份
由于当前不是特权用户,不能随意切换,所以要受到约束。
获取用户id:
X=getuid()
Y=geteuid()
a)挂起:
setuid(X)
恢复:
setuid(Y)
b) 永久放弃set-user-ID
setreuid(-1,X)
9-5 如果当前用户凭证如下,则要如何执行上面的操作?
real = X effective = 0 saved = 0
当前用户是特权用户。
获取用户id:
X=getuid()
Y=geteuid()
a)挂起:
seteuid(X)
恢复:
seteuid(0)
b)永久放弃权限:
setuid(X)