功能实现:shell中运行客户端程序临时获取root权限。
在linux下只允许一个服务器进程接收信息
使用文件锁,使服务器程序单实例运行(UNIX Domain Socket IPC):
/* server.c */
#include <stdio.h>
#include <stdlib.h>
#include <string.h>
#include <unistd.h>
#include <sys/socket.h>
#include <netinet/in.h>
#include <stddef.h>
#include <sys/un.h>
#include <fcntl.h>
#include <error.h>
#include <libgen.h>
#include <dirent.h>
#include <errno.h>
#define MAXLINE 1024
//返回当前可执行文件名
char *currentExeName()
{
long pid;
char full_name[MAXLINE],proc_name[MAXLINE];
bzero(full_name,MAXLINE);
bzero(proc_name,MAXLINE);
int fd;
pid = getpid();
printf("pid = %ld\n",pid);
sprintf(full_name, "/proc/%ld/cmdline", pid);
if (access(full_name, F_OK) == 0) {
fd = open(full_name, O_RDONLY);
if (fd == -1){
perror("fd open failed...\n");
return 0;
}
read(fd, proc_name, MAXLINE);
close(fd);
}
else {
return 0;
}
printf("proc_name = %s\n",proc_name);
char self_proc_name[512];
bzero(self_proc_name,512);
char *p = proc_name;
int pt = 0;
while(*p != ' ' && *p != '\0') {
self_proc_name[pt] = *p;
p++;
pt++;
}
printf("self_proc_name = %s\n",self_proc_name);
char self_final_name[512];
bzero(self_final_name,512);
char *sfn;
sfn = basename(self_proc_name);
strncpy(self_final_name,sfn,sizeof(sfn));
printf("self_final_name = %s\n",self_final_name);
return sfn;
}
int runSingleInstance()
{
// 获取当前可执行文件名
char *processName = currentExeName();
if (processName == NULL)
{
printf("processName is null...\n");
return 0;
}
// 打开或创建一个文件
char filePath[MAXLINE];
bzero(filePath,sizeof(MAXLINE));
sprintf(filePath,"/var/run/%s.pid",processName);
int fd = open(filePath, O_RDWR | O_CREAT, 0666);
if (fd < 0)
{
perror("Open file failed, error...\n");
return 0;
}
// 将该文件锁定
// 锁定后的文件将不能够再次锁定
struct flock fl;
fl.l_type = F_WRLCK; // 写文件锁定
fl.l_start = 0;
fl.l_whence = SEEK_SET;
fl.l_len = 0;
int ret = fcntl(fd, F_SETLK, &fl);
if (ret < 0)
{
if (errno == EACCES || errno == EAGAIN)
{
printf("%s already locked, error: %s\n", filePath, strerror(errno));
close(fd);
return 0;
}
}
// 锁定文件后,将该进程的pid写入文件
char buf[16] = {'\0'};
sprintf(buf, "%d", getpid());
ftruncate(fd, 0);
ret = write(fd, buf, strlen(buf));
if (ret < 0)
{
printf("Write file failed, file: %s, error: %s\n", filePath, strerror(errno));
close(fd);
return 0;
}
// 函数返回时不需要调用close(fd)
// 不然文件锁将失效
// 程序退出后kernel会自动close
return 1;
}
int main(void){
//单实例,判断服务器是否已经在运行
if (!runSingleInstance()) {
perror("server is already running...\n");
return 0;
}else{
printf("server is not already running...\n");
}
listenfd = socket(AF_UNIX, SOCK_STREAM, 0);
if(listenfd < 0){
perror ("can not create listen socket ...\n");
exit(1);
}
servaddr.sun_family = AF_UNIX;
strcpy(servaddr.sun_path, "foo.socket");
servaddr_size = offsetof(struct sockaddr_un, sun_path) + strlen(servaddr.sun_path);
bind(listenfd, (struct sockaddr *)&servaddr, sizeof(servaddr));
//修改服务端socket权限
chmod ("foo.socket", 0666);
//监听端口
tmp = listen(listenfd, 20);
if(tmp < 0){
perror ("listen failed...\n");
close(listenfd);
exit(1);
}
printf("Accepting connections ...\n");
while (1) {
cliaddr_len = sizeof(cliaddr);
//接收连接申请
connfd = accept(listenfd,(struct sockaddr *)&cliaddr, &cliaddr_len);
if (connfd < 0){
perror("accept failed...\n");
continue;
}
pid = fork();
if (pid < 0) {
perror("fork failed");
exit(-1);
}
if (pid > 0){
printf ("parent pid = %d, ppid = %d\n",getpid(),getppid());
}else{
printf ("child pid = %d, ppid = %d\n",getpid(),getppid());
while(1) {
bzero(buf,MAXLINE);
tmp = read(connfd, buf, MAXLINE);
if (tmp == -1){
break;
}else if(tmp == buf[0] && tmp>2){
printf("received:%s\n",&buf[2]);
if (buf[1] == 0){
FILE* fp = NULL;
fp = popen(&buf[2], "r");
if (!fp) {
perror("popen failed...\n");
exit(-1);
}
char t_buf[MAXLINE];
while ((tmp = fread(t_buf,1,sizeof(t_buf),fp))> 0)
{
write(connfd,t_buf,tmp);
bzero(t_buf,sizeof(t_buf));
}
write(connfd,t_buf,1);
pclose(fp);
}else if (buf[1] == 2){
// printf ("buf[1] = 2...buf[2] = %s\n",&buf[2]);
if (strncmp (&buf[2] , "cd ", 3) == 0) {
// printf ("strncmp (&buf[2] , cd, 3) == 0...buf[5]=%s\n",&buf[5]);
if (is_dir_exist(&buf[5])) {
// printf ("is_dir_exist...\n");
chdir(&buf[5]);
}
char t_buf[16];
bzero(t_buf, sizeof(t_buf));
write (connfd, t_buf, 1);
}else{
break;
}
}else
{
break;
}
}else
{
break;
}
}
close(connfd);
exit(1);
}
close(connfd);
}
close(listenfd);
return 0;
}
//判断文件夹是否存在
int is_dir_exist (const char* dir) {
if (dir == NULL) {
return 0;
}
if (opendir(dir) == NULL) {
return 0;
}
return 1;
}
对应的客户端程序:
/* client.c */
#include <stdio.h>
#include <stdlib.h>
#include <string.h>
#include <unistd.h>
#include <sys/socket.h>
#include <netinet/in.h>
#include <stddef.h>
#include <sys/un.h>
#include <pwd.h>
#include <dirent.h>
// #define SERV_PORT 8000
#define CMD_BUFFER_SIZE 256
#define RET_BUFFER_SIZE 1024
int sockfd;
int main(int argc, char *argv[])
{
struct sockaddr_un servaddr, cliaddr;
int servaddr_size, cliaddr_size;
int tmp;
//创建socket
sockfd = socket(AF_UNIX, SOCK_STREAM, 0);
if(sockfd < 0){
perror("can't creat socket");
return -1;
}
cliaddr.sun_family = AF_UNIX;
strcpy(cliaddr.sun_path, "roo.socket");
cliaddr_size = offsetof(struct sockaddr_un, sun_path) + strlen(cliaddr.sun_path);
//绑定客户端socket
bind(sockfd, (struct sockaddr *)&cliaddr, cliaddr_size);
servaddr.sun_family = AF_UNIX;
strcpy(servaddr.sun_path, "foo.socket");
servaddr_size = offsetof(struct sockaddr_un, sun_path) + strlen(servaddr.sun_path);
//申请连接
tmp = connect(sockfd, (struct sockaddr *)&servaddr,servaddr_size);
if (tmp < 0) {
perror("cannot connect server");
close(sockfd);
return -1;
}
char cmd_buff[CMD_BUFFER_SIZE];
memset (cmd_buff, 0 , sizeof(cmd_buff));
if (argc == 1) {
if (geteuid() == 0) {
setuid(0);
}
char path[256];
int pos = 0;
memset(path, 0, sizeof(path));
getcwd(path, sizeof(path));
sprintf(cmd_buff, "cd %s", path);
tmp = send_command (cmd_buff, 2);
while (1) {
struct passwd *pwd;
pwd = getpwuid(getuid());
memset(path, 0, sizeof(path));
getcwd(path, sizeof(path));
printf("%s@cct_mode:%s $ ", pwd->pw_name, path);
memset (cmd_buff, 0 , sizeof(cmd_buff));
int ch;
while (pos+2 < CMD_BUFFER_SIZE && (ch = getchar()) != '\n') {
cmd_buff[pos++] = ch;
}
//后续添加处理间隔符代码
if (strcmp (cmd_buff, "exit") == 0) {
break;
} else if (strncmp (cmd_buff, "cd ", 3) == 0) {
if (is_dir_exist(&cmd_buff[3])) {
if (chdir(&cmd_buff[3]) == 0) {
tmp = send_command (cmd_buff, 2);
}
}
} else {
tmp = send_command (cmd_buff, 0);
}
// memset (cmd_buff, 0 , sizeof(cmd_buff));
pos = 0;
}
}else {
int pos = 0;
int i;
for (i = 1; i < argc; i++) {
if (pos + strlen(argv[i]) + 2 >= CMD_BUFFER_SIZE) {
printf ("som_shell : error, command is too long......\n");
return -1;
}
strcat(&cmd_buff[pos], argv[i]);
pos += strlen(argv[i]);
if (i < argc-1) {
cmd_buff[pos] = ' ';
pos++;
}
}
tmp = send_command (cmd_buff, 0);
}
close(sockfd);
return tmp;
}
int is_dir_exist (const char* dir) {
if (dir == NULL) {
return 0;
}
if (opendir(dir) == NULL) {
return 0;
}
return 1;
}
/**
* @brif 通过socket向server发送指令
* @param cmd 待发送指令
* @param type 指令类型 0:普通, 2:内建指令
* @return 成功返回0,失败返回-1
**/
int send_command (char* cmd, int type) {
int ret;
char* send_buff;
char ret_buff[RET_BUFFER_SIZE];
int len = strlen(cmd) + 2;
if (len > CMD_BUFFER_SIZE) {
printf("error, command is too long......\n");
return -1;
} else {
send_buff = malloc(len);
}
memset(ret_buff, 0, sizeof(ret_buff));
memcpy (&send_buff[2], cmd, strlen(cmd));
send_buff[0] = len;
send_buff[1] = type;
int n;
n = write(sockfd, send_buff, len);
printf("write return:%d\n",n);
while ((ret = read(sockfd, ret_buff, sizeof(ret_buff))) > 0) {
int i;
for (i = 0; i<ret; i++) {
putchar(ret_buff[i]);
}
//接收到终止符
if (ret_buff[ret-1] == 0) {
break;
}
memset(ret_buff, 0, sizeof(ret_buff));
}
free(send_buff);
return 0;
}