silk/modules/ulib.c

910 lines
17 KiB
C
Raw Normal View History

#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_chmod(lua_State *L)
{
const char *path = luaL_checkstring(L, 1);
const char *mode_str = luaL_checkstring(L, 2);
int mode = strtol(mode_str, 0, 8);
if (chmod(path, mode) < 0)
{
lua_pushboolean(L, 0);
lua_pushstring(L, strerror(errno));
}
else
{
lua_pushboolean(L, 1);
lua_pushnil(L);
}
return 2;
}
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},
{"chmod", l_chmod},
{"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;
}