silk/modules/ulib.c

867 lines
16 KiB
C

#ifdef LINUX
#include <unistd.h>
#include <limits.h>
#include <pwd.h>
#include <shadow.h>
#include <fcntl.h>
#include <sys/sendfile.h>
#endif
#include <stdint.h>
#include <stdio.h>
#include <stdlib.h>
#include <time.h>
#include <dirent.h>
#include <sys/stat.h>
#include <sys/types.h>
#include <sys/wait.h>
#include <signal.h>
#include <errno.h>
#include <pwd.h>
#include <grp.h>
#include <ctype.h>
#include "lua/lualib.h"
// zip library
#include "3rd/zip/zip.h"
#define MAX_PATH_LEN 1024
/**
* Trim a string by a character on both ends
* @param str The target string
* @param delim the delim
*/
static void trim(char* str, const char delim)
{
if(!str || strlen(str) == 0) return;
char * p = str;
int l = strlen(p);
while(l > 0 && p[l - 1] == delim)
p[--l] = 0;
while(* p && (* p) == delim ) ++p, --l;
memmove(str, p, l + 1);
}
static int l_check_login (lua_State *L) {
#ifdef LINUX
const char* username = luaL_checkstring(L,1);
const char* password = luaL_checkstring(L,2);
char *encrypted;
struct passwd *pwd;
struct spwd *spwd;
/* Look up password and shadow password records for username */
pwd = getpwnam(username);
if (pwd == NULL)
{
lua_pushboolean(L,0);
lua_pushstring(L, strerror(errno));
return 2;
}
spwd = getspnam(username);
/*if (spwd == NULL)
{
lua_pushboolean(L,0);
printf("no permission to read shadow password file\n");
return 1;
}*/
if (spwd != NULL) /* If there is a shadow password record */
{
pwd->pw_passwd = spwd->sp_pwdp; /* Use the shadow password */
}
/*else
{
lua_pushboolean(L,0);
printf("shadow record is null \n" );
return 1;
}*/
/* Encrypt password and erase cleartext version immediately */
encrypted = crypt(password, pwd->pw_passwd);
if (encrypted == NULL)
{
lua_pushboolean(L,0);
lua_pushstring(L, strerror(errno));
return 2;
}
if(strcmp(encrypted, pwd->pw_passwd) == 0)
{
lua_pushboolean(L,1);
return 1;
} else
{
lua_pushboolean(L,0);
return 1;
}
#else
// macos
// just pass the check, for test only
lua_pushboolean(L,0);
lua_pushstring(L, "Login by shadow passwd is not supported on this system");
return 2;
#endif
}
static void get_userId(const char* name, uid_t* uid,gid_t* gid )
{
struct passwd *pwd;
uid_t u;
char *endptr;
if (name == NULL || *name == '\0')
{
*uid = -1; *gid=-1;
return;
}
u = strtol(name, &endptr, 10);
if (*endptr == '\0')
{
*uid = u;
*gid = -1;
return;
}
pwd = getpwnam(name);
if (pwd == NULL)
{
*uid = -1; *gid=-1;
return;
}
*uid = pwd->pw_uid;
*gid = pwd->pw_gid;
}
static int l_fork(lua_State* L)
{
int pid = fork();
lua_pushnumber(L, pid);
return 1;
}
static int l_waitpid(lua_State* L)
{
int pid = luaL_checknumber(L,1);
int nohang = luaL_checknumber(L,2);
pid_t st;
int status;
if(nohang)
{
st = waitpid(pid, &status, WNOHANG);
}
else
{
st = waitpid(pid, &status, 0);
}
lua_pushnumber(L, st);
return 1;
}
static int l_kill(lua_State* L)
{
int pid = luaL_checknumber(L,1);
if(pid == -1) pid = getpid();
int status = kill(pid, SIGHUP);
lua_pushnumber(L, status);
return 1;
}
static int l_setuid(lua_State* L)
{
uid_t uid = (uid_t) luaL_checknumber(L,1);
if((int)uid != -1)
{
if(setuid(uid) < 0)
{
lua_pushboolean(L,0);
lua_pushstring(L, strerror(errno));
return 2;
}
else
{
//printf("UID set\n");
lua_pushboolean(L,1);
return 1;
}
}
else
lua_pushboolean(L,0);
return 1;
}
static int l_setgid(lua_State* L)
{
uid_t gid = (uid_t) luaL_checknumber(L,1);
if((int)gid != -1)
{
if(setgid(gid) < 0)
{
lua_pushboolean(L,0);
lua_pushstring(L, strerror(errno));
return 2;
}
else
{
//printf("GID set\n");
lua_pushboolean(L,1);
return 1;
}
}
else
lua_pushboolean(L,0);
return 1;
}
static int l_getuid(lua_State* L)
{
const char* name = luaL_checkstring(L,1);
uid_t uid = -1;
uid_t gid = -1;
get_userId(name,&uid,&gid);
lua_newtable(L);
lua_pushstring(L,"id");
lua_pushnumber(L,uid);
lua_settable(L,-3);
lua_pushstring(L,"gid");
lua_pushnumber(L,gid);
lua_settable(L,-3);
int j, ngroups = 30; //only first 10 group
gid_t *groups;
struct group *gr;
// find all the groups
if((int)uid != -1 && (int)gid != -1)
{
/* Retrieve group list */
groups = malloc(ngroups * sizeof (gid_t));
if (groups == NULL) {
return 1;
}
if (getgrouplist(name, gid, groups, &ngroups) == -1) {
free(groups);
return 1;
}
/* retrieved groups, along with group names */
lua_pushstring(L,"groups");
lua_newtable(L);
for (j = 0; j < ngroups; j++) {
gr = getgrgid(groups[j]);
if (gr != NULL)
{
//printf("%d: (%s)\n", groups[j],gr->gr_name);
lua_pushnumber(L, groups[j]);
lua_pushstring(L, gr->gr_name);
lua_settable(L,-3);
}
}
lua_settable(L, -3);
free(groups);
}
return 1;
}
static void timestr(time_t time, char* buf,int len,char* format, int gmt)
{
struct tm t;
if(gmt)
{
gmtime_r(&time, &t);
}
else
{
localtime_r(&time, &t);
}
strftime(buf, len, format, &t);
}
static int l_file_stat(lua_State* L, const char* path)
{
//const char* path = luaL_checkstring(L,-1);
//printf("PATH %s\n", path);
//lua_pop(L,1);
char date[64];
struct stat st;
if( stat(path, &st) == 0 )
{
// recore for file
lua_newtable(L);
//type
lua_pushstring(L,"type");
if(S_ISDIR(st.st_mode))
lua_pushstring(L,"dir");
else
lua_pushstring(L,"file");
lua_settable(L,-3);
//ctime
lua_pushstring(L,"ctime");
timestr(st.st_ctime,date,sizeof(date),"%a, %d %b %Y %H:%M:%S GMT",1);
lua_pushstring(L,date);
lua_settable(L,-3);
//mtime
lua_pushstring(L,"mtime");
timestr(st.st_mtime,date,sizeof(date),"%a, %d %b %Y %H:%M:%S GMT",1);
lua_pushstring(L,date);
lua_settable(L,-3);
//size
lua_pushstring(L,"size");
lua_pushnumber(L,st.st_size);
lua_settable(L,-3);
// uid
lua_pushstring(L,"uid");
lua_pushnumber(L,st.st_uid);
lua_settable(L,-3);
// gid
lua_pushstring(L,"gid");
lua_pushnumber(L,st.st_gid);
lua_settable(L,-3);
lua_pushstring(L,"permissions");
sprintf(date," (%3o)", st.st_mode&0777);
lua_pushstring(L,date);
lua_settable(L,-3);
// permission
lua_pushstring(L,"perm");
lua_newtable(L);
lua_pushstring(L,"owner");
lua_newtable(L);
lua_pushstring(L,"read");
lua_pushboolean(L, (st.st_mode & S_IRUSR)==0?0:1);
lua_settable(L,-3);
lua_pushstring(L,"write");
lua_pushboolean(L, (st.st_mode & S_IWUSR)==0?0:1);
lua_settable(L,-3);
lua_pushstring(L,"exec");
lua_pushboolean(L, (st.st_mode & S_IXUSR)==0?0:1);
lua_settable(L,-3);
//end owner
lua_settable(L,-3);
lua_pushstring(L,"group");
lua_newtable(L);
lua_pushstring(L,"read");
lua_pushboolean(L, (st.st_mode & S_IRGRP)==0?0:1);
lua_settable(L,-3);
lua_pushstring(L,"write");
lua_pushboolean(L, (st.st_mode & S_IWGRP)==0?0:1);
lua_settable(L,-3);
lua_pushstring(L,"exec");
lua_pushboolean(L, (st.st_mode & S_IXGRP)==0?0:1);
lua_settable(L,-3);
//end owner
lua_settable(L,-3);
lua_pushstring(L,"other");
lua_newtable(L);
lua_pushstring(L,"read");
lua_pushboolean(L, (st.st_mode & S_IROTH)==0?0:1);
lua_settable(L,-3);
lua_pushstring(L,"write");
lua_pushboolean(L, (st.st_mode & S_IWOTH)==0?0:1);
lua_settable(L,-3);
lua_pushstring(L,"exec");
lua_pushboolean(L, (st.st_mode & S_IXOTH)==0?0:1);
lua_settable(L,-3);
//end owner
lua_settable(L,-3);
// end permission
lua_settable(L,-3);
}
else
{
lua_newtable(L);
}
return 1;
}
static int l_file_info(lua_State* L)
{
const char* path = luaL_checkstring(L,1);
l_file_stat(L,path);
return 1;
}
static int l_file_move(lua_State* L)
{
const char* old = luaL_checkstring(L,1);
const char* new = luaL_checkstring(L,2);
//printf("MOVE %s to %s\n",old,new);
lua_pushboolean(L, rename(old,new)==0);
return 1;
}
static int l_send_file(lua_State* L)
{
int fromfd, tofd, ret;
size_t sz = 0;
char* old = NULL;
char* new = NULL;
if(lua_isnumber(L,1))
{
fromfd = (int)luaL_checknumber(L,1);
}
else
{
old = (char*)luaL_checkstring(L,1);
if((fromfd = open(old, O_RDONLY)) < 0)
{
lua_pushboolean(L,0);
goto end_send_file;
}
struct stat st;
if(stat(old, &st)!=0)
{
lua_pushboolean(L,0);
goto end_send_file;
}
sz = st.st_size;
}
if(lua_isnumber(L,2))
{
tofd = (int) luaL_checknumber(L,2);
}
else
{
new = (char*)luaL_checkstring(L,2);
if (unlink(new) < 0 && errno != ENOENT) {
lua_pushboolean(L,0);
goto end_send_file;
}
if((tofd = open(new, O_WRONLY | O_CREAT, 0600)) < 0)
{
lua_pushboolean(L,0);
goto end_send_file;
}
}
if(lua_isnumber(L,3))
{
sz = (size_t) luaL_checknumber(L,3);
}
off_t off = 0;
int read = 0;
while (
sz > 0 &&
read != sz &&
(
((ret = sendfile(tofd, fromfd, &off, sz - read)) >= 0) ||
(errno == EAGAIN)
)
)
{
if(ret < 0) ret = 0;
read += ret;
}
if(read != sz)
{
lua_pushboolean(L,0);
goto end_send_file;
}
lua_pushboolean(L,1);
end_send_file:
if(fromfd >= 0 && old)
{
(void) close(fromfd);
}
if(tofd >= 0 && new)
{
(void) close(tofd);
}
return 1;
}
static int l_read_dir(lua_State* L)
{
const char* path = luaL_checkstring(L,1);
const char* prefix = luaL_checkstring(L,2);
DIR *d;
struct dirent *dir;
d = opendir(path);
char buff[1024];
lua_newtable(L);
if (d)
{
int id=0;
while ((dir = readdir(d)) != NULL)
{
//ignore curent directory, parent directory
if(strcmp(dir->d_name,".") == 0 ||
strcmp(dir->d_name,"..")==0/*|| *(dir->d_name)=='.'*/) continue;
sprintf(buff,"%s/%s",path,dir->d_name);
lua_pushnumber(L,id);
//lua_pushstring(L,buff);
l_file_stat(L,buff);
lua_pushstring(L,"filename");
lua_pushstring(L,dir->d_name);
lua_settable(L,-3);
sprintf(buff,"%s/%s",prefix,dir->d_name);
//printf("FILE %s\n",buff );
lua_pushstring(L,"path");
lua_pushstring(L,buff);
lua_settable(L,-3);
lua_settable(L,-3);
id++;
}
closedir(d);
}
else
{
lua_pushstring(L,"error");
lua_pushstring(L,"Resource not found");
lua_settable(L,-3);
}
return 1;
}
static int l_chown(lua_State* L)
{
const char* path = luaL_checkstring(L,1);
int id = luaL_checknumber(L,2);
int gid = luaL_checknumber(L,3);
lua_pushboolean(L, chown(path,id,gid) == 0);
return 1;
}
static int l_mkdir(lua_State* L)
{
const char* path = luaL_checkstring(L,1);
lua_pushboolean(L, mkdir(path, S_IRWXU | S_IRWXG | S_IROTH | S_IXOTH) == 0);
return 1;
}
static int l_exist(lua_State* L)
{
const char* path = luaL_checkstring(L,1);
lua_pushboolean(L,access( path, F_OK ) != -1 );
return 1;
}
static int is_dir(const char* f)
{
struct stat st;
if(stat(f, &st) == -1)
return -1; // unknow
else if(st.st_mode & S_IFDIR)
return 1;
else
return 0;
}
static int l_is_dir(lua_State *L)
{
const char *file = (char *)luaL_checkstring(L, 1);
lua_pushboolean(L, is_dir(file) == 1);
return 1;
}
static int _recursive_delete(const char* path)
{
if(is_dir(path) == 1)
{
DIR *d;
struct dirent *dir;
d = opendir(path);
char buff[1024];
if (d)
{
while ((dir = readdir(d)) != NULL)
{
//ignore current & parent dir
if(strcmp(dir->d_name,".") == 0 || strcmp(dir->d_name,"..")==0) continue;
// get the file
sprintf(buff,"%s/%s",path,dir->d_name);
if(_recursive_delete(buff) == -1) return -1;
}
closedir(d);
// remove dir
if(rmdir(path) != 0) return -1;
} else return -1;
}
else
{
// file remove
if(remove(path) != 0) return -1;
}
return 0;
}
static int l_delete(lua_State* L)
{
const char* f = luaL_checkstring(L,1);
lua_pushboolean(L,_recursive_delete(f) == 0);
return 1;
}
/*
* Only use this for simple command
* Be careful to expose this function
* to user. This can cause a serious
* security problem
*/
static int l_cmd_open(lua_State* L)
{
FILE *fp;
const char* cmd = luaL_checkstring(L,1);
/* Open the command for reading. */
fp = popen(cmd, "r");
if (fp == NULL) {
lua_pushnil(L);
return 1;
}
/*return the fd to lua
* user can use this to
read the output data*/
intptr_t pt = (intptr_t)fp;
lua_pushnumber(L, pt);
return 1;
}
static int l_cmd_read(lua_State* L)
{
/* Read the output a line at a time - output it.
read user defined data of std
*/
FILE * fd;
intptr_t pt = (intptr_t)luaL_checknumber(L, 1);
fd = (FILE*)pt;
if(fd == NULL)
{
lua_pushnil(L);
return 1;
}
char buff[1024];
if(fgets(buff, sizeof(buff)-1, fd) != NULL) {
lua_pushstring(L,buff);
} else
{
lua_pushnil(L);
}
/* close */
//pclose(fp);
return 1;
}
static int l_cmd_close(lua_State* L)
{
/* Read the output a line at a time - output it.
read user defined data of std
*/
FILE * fd;
intptr_t pt = (intptr_t)luaL_checknumber(L, 1);
fd = (FILE*)pt;
if(fd == NULL)
{
pclose(fd);
}
return 0;
}
/*zip file handler
* using miniz to compress and uncompress zip file
*/
static int l_unzip(lua_State* L)
{
const char* src = luaL_checkstring(L,1);
const char* dest = luaL_checkstring(L,2);
if(zip_extract(src, dest, NULL, NULL) == -1)
{
lua_pushboolean(L,0);
return 1;
}
lua_pushboolean(L,1);
return 1;
}
static int _add_to_zip(struct zip_t * zip, const char* path, const char* root)
{
int st = is_dir(path);
if(st == -1) return -1;
char p1[MAX_PATH_LEN];
char p2[MAX_PATH_LEN];
if(st)
{
// read directory
DIR *d;
struct dirent *dir;
//struct stat st;
d = opendir(path);
if (d)
{
while ((dir = readdir(d)) != NULL)
{
//ignore curent directory, parent directory
if(strcmp(dir->d_name,".") == 0 || strcmp(dir->d_name,"..")==0) continue;
// add file to zip
snprintf(p1,MAX_PATH_LEN,"%s/%s", path, dir->d_name);
snprintf(p2,MAX_PATH_LEN,"%s/%s",root,dir->d_name);
if(_add_to_zip(zip,p1,p2) == -1)
{
return -1;
}
}
closedir(d);
}
}
else
{
// if it is a file
// add it to zip
if(zip_entry_open(zip, root) == -1) return -1;
if(zip_entry_fwrite(zip, path) == -1) return -1;
zip_entry_close(zip);
}
return 0;
}
static int l_zip(lua_State* L)
{
const char* src = luaL_checkstring(L,1);
const char* dest = luaL_checkstring(L,2);
// create a zip handler
struct zip_t *zip = zip_open(dest, ZIP_DEFAULT_COMPRESSION_LEVEL, 0);
if(zip == NULL) goto pfalse;
if(_add_to_zip(zip,src,"") == -1) goto pfalse;
zip_close(zip);
lua_pushboolean(L,1);
return 1;
// return false
pfalse:
// TODO remove if filed is created
if(zip) zip_close(zip);
lua_pushboolean(L,0);
return 1;
}
static int l_getenv(lua_State* L)
{
const char* name = luaL_checkstring(L,1);
if(!name)
{
lua_pushnil(L);
return 1;
}
char * value = getenv(name);
lua_pushstring(L, value);
return 1;
}
static int l_setenv(lua_State* L)
{
const char* name = luaL_checkstring(L,1);
const char* value = luaL_checkstring(L,2);
const int force = luaL_checknumber(L,3);
if(!name)
{
lua_pushboolean(L, 0);
return 1;
}
if(!value)
{
lua_pushboolean(L, 0);
return 1;
}
int ret = setenv(name, value, force);
if(ret != 0)
{
lua_pushboolean(L, 0);
return 1;
}
lua_pushboolean(L,1);
return 1;
}
static int l_unsetenv(lua_State* L)
{
const char* name = luaL_checkstring(L,1);
if(!name)
{
lua_pushboolean(L, 0);
return 1;
}
int ret = unsetenv(name);
if(ret != 0)
{
lua_pushboolean(L, 0);
return 1;
}
lua_pushboolean(L,1);
return 1;
}
static int l_gethomedir(lua_State* L)
{
uid_t uid = (uid_t) luaL_checknumber(L,1);
if(uid < 0)
{
lua_pushnil(L);
return 1;
}
struct passwd *pw = getpwuid(uid);
if (pw == NULL) {
lua_pushnil(L);
return 1;
}
lua_pushstring(L, pw->pw_dir);
return 1;
}
static int l_trim(lua_State *L)
{
char *str = strdup((char *)luaL_checkstring(L, 1));
const char *tok = luaL_checkstring(L, 2);
trim(str, tok[0]);
lua_pushstring(L, (const char *)str);
free(str);
return 1;
}
static const struct luaL_Reg _lib [] = {
{"auth", l_check_login},
{"read_dir",l_read_dir},
{"file_stat",l_file_info},
{"uid",l_getuid},
{"setuid", l_setuid},
{"setgid", l_setgid},
{"fork", l_fork},
{"kill", l_kill},
{"waitpid", l_waitpid},
{"trim", l_trim},
{"chown",l_chown},
{"delete", l_delete},
{"exists",l_exist},
{"mkdir",l_mkdir},
{"cmdopen",l_cmd_open},
{"cmdread",l_cmd_read},
{"cmdclose",l_cmd_close},
{"move",l_file_move},
{"unzip",l_unzip},
{"zip",l_zip},
{"getenv",l_getenv},
{"setenv",l_setenv},
{"unsetenv",l_unsetenv},
{"home_dir",l_gethomedir},
{"send_file", l_send_file},
{"is_dir", l_is_dir},
{NULL,NULL}
};
int luaopen_ulib(lua_State *L)
{
luaL_newlib(L, _lib);
return 1;
}