forked from public/pyceo
parent
4ec2fceaca
commit
3d20172dad
@ -0,0 +1,4 @@ |
||||
*.o |
||||
/addmember |
||||
/addclub |
||||
/config-test |
@ -0,0 +1,20 @@ |
||||
CFLAGS ?= -ggdb -Wall -O2
|
||||
CFLAGS += -I../include
|
||||
LDAP := -lldap
|
||||
KADM := $(shell krb5-config --libs krb5 kadm-client)
|
||||
|
||||
LIBCEO := util.o common.o config.o parser.o ldap.o krb5.o kadm.o addhomedir.o
|
||||
|
||||
all: addmember addclub |
||||
|
||||
clean: |
||||
rm -f addmember addclub config-test *.o
|
||||
|
||||
addmember: $(LIBCEO) addmember.o |
||||
$(CC) $(LDFLAGS) $(LDAP) $(KADM) $^ -o $@
|
||||
|
||||
addclub: $(LIBCEO) addclub.o |
||||
$(CC) $(LDFLAGS) $(LDAP) $(KADM) $^ -o $@
|
||||
|
||||
config-test: config-test.o parser.o util.o |
||||
$(CC) $(LDFLAGS) $^ -o $@
|
@ -0,0 +1,181 @@ |
||||
#include <unistd.h> |
||||
#include <sys/types.h> |
||||
#include <sys/wait.h> |
||||
#include <stdio.h> |
||||
#include <stdlib.h> |
||||
#include <getopt.h> |
||||
#include <ctype.h> |
||||
#include <pwd.h> |
||||
#include <grp.h> |
||||
#include <errno.h> |
||||
#include <libgen.h> |
||||
#include <syslog.h> |
||||
|
||||
#include "util.h" |
||||
#include "common.h" |
||||
#include "config.h" |
||||
#include "ldap.h" |
||||
#include "krb5.h" |
||||
#include "kadm.h" |
||||
#include "addhomedir.h" |
||||
|
||||
char *prog = NULL; |
||||
char *user = NULL; |
||||
int privileged = 0; |
||||
|
||||
static int force = 0; |
||||
static int no_notify = 0; |
||||
|
||||
static char *name = NULL; |
||||
static char *userid = NULL; |
||||
|
||||
static struct option opts[] = { |
||||
{ "force", 0, NULL, 'f' }, |
||||
{ "no-notify", 0, NULL, 'q' }, |
||||
{ NULL, 0, NULL, '\0' }, |
||||
}; |
||||
|
||||
static void usage() { |
||||
fprintf(stderr, "Usage: %s userid clubname\n", prog); |
||||
exit(2); |
||||
} |
||||
|
||||
void addclub() { |
||||
int krb_ok, user_ok, group_ok, home_ok, quota_ok; |
||||
int id; |
||||
char homedir[1024]; |
||||
|
||||
logmsg("adding uid=%s cn=%s by %s", userid, name, user); |
||||
|
||||
if (setreuid(0, 0)) |
||||
fatalpe("setreuid"); |
||||
|
||||
if (!force && getpwnam(userid) != NULL) |
||||
deny("user %s already exists", userid); |
||||
|
||||
ceo_krb5_init(); |
||||
ceo_ldap_init(); |
||||
ceo_kadm_init(); |
||||
|
||||
if (ceo_user_exists(userid)) |
||||
deny("user %s already exists in LDAP", userid); |
||||
|
||||
if ((id = ceo_new_uid(member_min_id, member_max_id)) <= 0) |
||||
fatal("no available uids in range [%d, %d]", member_min_id, member_max_id); |
||||
|
||||
snprintf(homedir, sizeof(homedir), "%s/%s", club_home, userid); |
||||
|
||||
krb_ok = ceo_del_princ(userid); |
||||
if (!krb_ok) |
||||
logmsg("successfully cleared principal for %s", userid); |
||||
|
||||
user_ok = krb_ok || ceo_add_user(userid, users_base, "club", name, homedir, |
||||
club_shell, id, NULL); |
||||
if (!user_ok) |
||||
logmsg("successfully created account for %s", userid); |
||||
|
||||
group_ok = user_ok || ceo_add_group(userid, groups_base, id); |
||||
if (!group_ok) |
||||
logmsg("successfully created group for %s", userid); |
||||
|
||||
home_ok = user_ok || ceo_create_home(homedir, id, id); |
||||
if (!home_ok) |
||||
logmsg("successfully created home directory for %s", userid); |
||||
|
||||
quota_ok = user_ok || ceo_set_quota(quota_prototype, id); |
||||
if (!quota_ok) |
||||
logmsg("successfully set quota for %s", userid); |
||||
|
||||
logmsg("done uid=%s", userid); |
||||
|
||||
if (!no_notify && !user_ok) { |
||||
int pid; |
||||
int hkp[2]; |
||||
FILE *hkf; |
||||
int status; |
||||
|
||||
if (pipe(hkp)) |
||||
errorpe("pipe"); |
||||
|
||||
fflush(stdout); |
||||
fflush(stderr); |
||||
|
||||
pid = fork(); |
||||
|
||||
if (!pid) { |
||||
fclose(stdout); |
||||
fclose(stderr); |
||||
close(hkp[1]); |
||||
dup2(hkp[0], 0); |
||||
exit(execl(notify_hook, notify_hook, prog, user, userid, name, NULL)); |
||||
} |
||||
|
||||
hkf = fdopen(hkp[1], "w"); |
||||
|
||||
if (group_ok) |
||||
fprintf(hkf, "failed to create group\n"); |
||||
if (home_ok) |
||||
fprintf(hkf, "failed to create home directory\n"); |
||||
if (quota_ok) |
||||
fprintf(hkf, "failed to set quota\n"); |
||||
if (!group_ok && !home_ok && !quota_ok) |
||||
fprintf(hkf, "all failures went undetected\n"); |
||||
|
||||
fclose(hkf); |
||||
|
||||
waitpid(pid, &status, 0); |
||||
|
||||
if (WIFEXITED(status) && WEXITSTATUS(status)) |
||||
logmsg("hook %s exited with status %d", notify_hook, WEXITSTATUS(status)); |
||||
else if (WIFSIGNALED(status)) |
||||
logmsg("hook %s killed by signal %d", notify_hook, WTERMSIG(status)); |
||||
} |
||||
|
||||
ceo_kadm_cleanup(); |
||||
ceo_ldap_cleanup(); |
||||
ceo_krb5_cleanup(); |
||||
|
||||
return !krb_ok && !user_ok && !group_ok && !home_ok && quota_ok; |
||||
} |
||||
|
||||
int main(int argc, char *argv[]) { |
||||
int opt; |
||||
|
||||
openlog(prog, 0, LOG_AUTHPRIV); |
||||
|
||||
configure(); |
||||
|
||||
prog = basename(argv[0]); |
||||
user = ceo_get_user(); |
||||
privileged = ceo_get_privileged(); |
||||
|
||||
while ((opt = getopt_long(argc, argv, "", opts, NULL)) != -1) { |
||||
switch (opt) { |
||||
case 'f': |
||||
if (!privileged) |
||||
deny("not privileged enough to force"); |
||||
force = 1; |
||||
break; |
||||
case 'q': |
||||
if (!privileged) |
||||
deny("not privileged enough to suppress notifications"); |
||||
no_notify = 1; |
||||
break; |
||||
case '?': |
||||
usage(); |
||||
break; |
||||
default: |
||||
fatal("error parsing arguments"); |
||||
} |
||||
} |
||||
|
||||
if (argc - optind != 2) |
||||
usage(); |
||||
|
||||
userid = argv[optind++]; |
||||
name = argv[optind++]; |
||||
|
||||
addclub(); |
||||
|
||||
return 0; |
||||
} |
@ -0,0 +1,133 @@ |
||||
#include <stdio.h> |
||||
#include <unistd.h> |
||||
#include <sys/types.h> |
||||
#include <sys/stat.h> |
||||
#include <dirent.h> |
||||
#include <pwd.h> |
||||
#include <fcntl.h> |
||||
|
||||
#include "addhomedir.h" |
||||
#include "util.h" |
||||
#include "config.h" |
||||
|
||||
int ceo_create_home(char *homedir, uid_t uid, gid_t gid) { |
||||
int mask; |
||||
DIR *skel; |
||||
struct dirent *skelent; |
||||
|
||||
mask = umask(0); |
||||
|
||||
if (mkdir(homedir, homedir_mode)) { |
||||
errorpe("failed to create %s", homedir); |
||||
return -1; |
||||
} |
||||
|
||||
skel = opendir(skeleton_dir); |
||||
if (!skel) { |
||||
errorpe("failed to open %s", skeleton_dir); |
||||
return -1; |
||||
} |
||||
|
||||
while ((skelent = readdir(skel))) { |
||||
struct stat sb; |
||||
char src[PATH_MAX], dest[PATH_MAX]; |
||||
|
||||
if (!strcmp(skelent->d_name, ".") || !strcmp(skelent->d_name, "..")) |
||||
continue; |
||||
|
||||
snprintf(src, sizeof(src), "%s/%s", skeleton_dir, skelent->d_name); |
||||
snprintf(dest, sizeof(dest), "%s/%s", homedir, skelent->d_name); |
||||
lstat(src, &sb); |
||||
|
||||
if (sb.st_uid || sb.st_gid) { |
||||
warn("not creating %s due to ownership", dest); |
||||
continue; |
||||
} |
||||
|
||||
if (S_ISREG(sb.st_mode)) { |
||||
int bytes; |
||||
char buf[4096]; |
||||
|
||||
int srcfd = open(src, O_RDONLY); |
||||
if (srcfd == -1) { |
||||
warnpe("open: %s", src); |
||||
continue; |
||||
} |
||||
|
||||
int destfd = open(dest, O_WRONLY|O_CREAT|O_EXCL, sb.st_mode & 0777); |
||||
if (destfd == -1) { |
||||
warnpe("open: %s", dest); |
||||
close(srcfd); |
||||
continue; |
||||
} |
||||
|
||||
for (;;) { |
||||
bytes = read(srcfd, buf, sizeof(buf)); |
||||
if (!bytes) |
||||
break; |
||||
if (bytes < 0) { |
||||
warnpe("read"); |
||||
break; |
||||
} |
||||
if (write(destfd, buf, bytes) < 0) { |
||||
warnpe("write"); |
||||
break; |
||||
} |
||||
} |
||||
|
||||
if (fchown(destfd, uid, gid)) |
||||
errorpe("chown: %s", dest); |
||||
|
||||
close(srcfd); |
||||
close(destfd); |
||||
} else if (S_ISDIR(sb.st_mode)) { |
||||
if (mkdir(dest, sb.st_mode & 0777)) { |
||||
warnpe("mkdir: %s", dest); |
||||
continue; |
||||
} |
||||
if (chown(dest, uid, gid)) |
||||
errorpe("chown: %s", dest); |
||||
} else if (S_ISLNK(sb.st_mode)) { |
||||
char lnkdest[PATH_MAX]; |
||||
int bytes; |
||||
bytes = readlink(src, lnkdest, sizeof(lnkdest)); |
||||
lnkdest[bytes] = '\0'; |
||||
if (bytes == -1) { |
||||
warnpe("readlink: %s", src); |
||||
continue; |
||||
} |
||||
if (symlink(lnkdest, dest)) { |
||||
warnpe("symlink: %s", dest); |
||||
continue; |
||||
} |
||||
if (lchown(dest, uid, gid)) |
||||
errorpe("lchown: %s", dest); |
||||
} else { |
||||
warn("not creating %s", dest); |
||||
} |
||||
} |
||||
|
||||
closedir(skel); |
||||
|
||||
if (chown(homedir, uid, gid)) |
||||
errorpe("failed to chown %s", homedir); |
||||
|
||||
umask(mask); |
||||
|
||||
return 0; |
||||
} |
||||
|
||||
int ceo_set_quota(char *proto, int id) { |
||||
char user[128]; |
||||
char *sqargs[] = { "setquota", "-a", "-p", proto, NULL, NULL }; |
||||
|
||||
snprintf(user, sizeof(user), "%d", id); |
||||
sqargs[4] = user; |
||||
|
||||
if (spawnv("/usr/sbin/setquota", sqargs)) { |
||||
error("failed to set quota for %s", user); |
||||
return -1; |
||||
} |
||||
|
||||
return 0; |
||||
} |
@ -0,0 +1,4 @@ |
||||
#include <sys/types.h> |
||||
|
||||
int ceo_create_home(char *, uid_t, gid_t); |
||||
int ceo_set_quota(char *, int); |
@ -0,0 +1,196 @@ |
||||
#include <unistd.h> |
||||
#include <sys/types.h> |
||||
#include <sys/wait.h> |
||||
#include <stdio.h> |
||||
#include <stdlib.h> |
||||
#include <getopt.h> |
||||
#include <ctype.h> |
||||
#include <pwd.h> |
||||
#include <grp.h> |
||||
#include <errno.h> |
||||
#include <libgen.h> |
||||
#include <syslog.h> |
||||
|
||||
#include "util.h" |
||||
#include "common.h" |
||||
#include "config.h" |
||||
#include "ldap.h" |
||||
#include "krb5.h" |
||||
#include "kadm.h" |
||||
#include "addhomedir.h" |
||||
|
||||
char *prog = NULL; |
||||
char *user = NULL; |
||||
int privileged = 0; |
||||
|
||||
static int force = 0; |
||||
static int no_notify = 0; |
||||
|
||||
static int use_stdin = 0; |
||||
|
||||
static char *name = NULL; |
||||
static char *userid = NULL; |
||||
static char *program = NULL; |
||||
static char password[1024]; |
||||
|
||||
static struct option opts[] = { |
||||
{ "force", 0, NULL, 'f' }, |
||||
{ "no-notify", 0, NULL, 'q' }, |
||||
{ "stdin", 0, NULL, 's' }, |
||||
{ NULL, 0, NULL, '\0' }, |
||||
}; |
||||
|
||||
static void usage() { |
||||
fprintf(stderr, "Usage: %s userid realname [program]\n", prog); |
||||
exit(2); |
||||
} |
||||
|
||||
void addmember() { |
||||
int krb_ok, user_ok, group_ok, home_ok, quota_ok; |
||||
int id; |
||||
char homedir[1024]; |
||||
|
||||
logmsg("adding uid=%s cn=%s program=%s by %s", userid, name, program, user); |
||||
|
||||
if (setreuid(0, 0)) |
||||
fatalpe("setreuid"); |
||||
|
||||
if (!force && getpwnam(userid) != NULL) |
||||
deny("user %s already exists", userid); |
||||
|
||||
if (ceo_read_password(password, sizeof(password), use_stdin)) |
||||
return; |
||||
|
||||
ceo_krb5_init(); |
||||
ceo_ldap_init(); |
||||
ceo_kadm_init(); |
||||
|
||||
if (ceo_user_exists(userid)) |
||||
deny("user %s already exists in LDAP", userid); |
||||
|
||||
if ((id = ceo_new_uid(member_min_id, member_max_id)) <= 0) |
||||
fatal("no available uids in range [%d, %d]", member_min_id, member_max_id); |
||||
|
||||
snprintf(homedir, sizeof(homedir), "%s/%s", member_home, userid); |
||||
|
||||
krb_ok = ceo_del_princ(userid); |
||||
krb_ok = krb_ok || ceo_add_princ(userid, password); |
||||
if (!krb_ok) |
||||
logmsg("successfully created principal for %s", userid); |
||||
|
||||
user_ok = krb_ok || ceo_add_user(userid, users_base, "member", name, homedir, |
||||
member_shell, id, "program", program, NULL); |
||||
if (!user_ok) |
||||
logmsg("successfully created account for %s", userid); |
||||
|
||||
group_ok = user_ok || ceo_add_group(userid, groups_base, id); |
||||
if (!group_ok) |
||||
logmsg("successfully created group for %s", userid); |
||||
|
||||
home_ok = user_ok || ceo_create_home(homedir, id, id); |
||||
if (!home_ok) |
||||
logmsg("successfully created home directory for %s", userid); |
||||
|
||||
quota_ok = user_ok || ceo_set_quota(quota_prototype, id); |
||||
if (!quota_ok) |
||||
logmsg("successfully set quota for %s", userid); |
||||
|
||||
logmsg("done uid=%s", userid); |
||||
|
||||
if (!no_notify && !user_ok) { |
||||
int pid; |
||||
int hkp[2]; |
||||
FILE *hkf; |
||||
int status; |
||||
|
||||
if (pipe(hkp)) |
||||
errorpe("pipe"); |
||||
|
||||
fflush(stdout); |
||||
fflush(stderr); |
||||
|
||||
pid = fork(); |
||||
|
||||
if (!pid) { |
||||
fclose(stdout); |
||||
fclose(stderr); |
||||
close(hkp[1]); |
||||
dup2(hkp[0], 0); |
||||
exit(execl(notify_hook, notify_hook, prog, user, userid, name, program, NULL)); |
||||
} |
||||
|
||||
hkf = fdopen(hkp[1], "w"); |
||||
|
||||
if (group_ok) |
||||
fprintf(hkf, "failed to create group\n"); |
||||
if (home_ok) |
||||
fprintf(hkf, "failed to create home directory\n"); |
||||
if (quota_ok) |
||||
fprintf(hkf, "failed to set quota\n"); |
||||
if (!group_ok && !home_ok && !quota_ok) |
||||
fprintf(hkf, "all failures went undetected\n"); |
||||
|
||||
fclose(hkf); |
||||
|
||||
waitpid(pid, &status, 0); |
||||
|
||||
if (WIFEXITED(status) && WEXITSTATUS(status)) |
||||
logmsg("hook %s exited with status %d", notify_hook, WEXITSTATUS(status)); |
||||
else if (WIFSIGNALED(status)) |
||||
logmsg("hook %s killed by signal %d", notify_hook, WTERMSIG(status)); |
||||
} |
||||
|
||||
ceo_kadm_cleanup(); |
||||
ceo_ldap_cleanup(); |
||||
ceo_krb5_cleanup(); |
||||
|
||||
return !krb_ok && !user_ok && !group_ok && !home_ok && quota_ok; |
||||
} |
||||
|
||||
int main(int argc, char *argv[]) { |
||||
int opt; |
||||
|
||||
openlog(prog, 0, LOG_AUTHPRIV); |
||||
|
||||
configure(); |
||||
|
||||
prog = basename(argv[0]); |
||||
user = ceo_get_user(); |
||||
privileged = ceo_get_privileged(); |
||||
|
||||
while ((opt = getopt_long(argc, argv, "", opts, NULL)) != -1) { |
||||
switch (opt) { |
||||
case 's': |
||||
use_stdin = 1; |
||||
break; |
||||
case 'f': |
||||
if (!privileged) |
||||
deny("not privileged enough to force"); |
||||
force = 1; |
||||
break; |
||||
case 'q': |
||||
if (!privileged) |
||||
deny("not privileged enough to suppress notifications"); |
||||
no_notify = 1; |
||||
break; |
||||
case '?': |
||||
usage(); |
||||
break; |
||||
default: |
||||
fatal("error parsing arguments"); |
||||
} |
||||
} |
||||
|
||||
if (argc - optind != 2 && argc - optind != 3) |
||||
usage(); |
||||
|
||||
userid = argv[optind++]; |
||||
name = argv[optind++]; |
||||
|
||||
if (argc - optind) |
||||
program = argv[optind++]; |
||||
|
||||
addmember(); |
||||
|
||||
return 0; |
||||
} |
@ -0,0 +1,58 @@ |
||||
#include <unistd.h> |
||||
#include <sys/types.h> |
||||
#include <pwd.h> |
||||
#include <grp.h> |
||||
|
||||
#include "common.h" |
||||
#include "util.h" |
||||
#include "config.h" |
||||
|
||||
int ceo_get_privileged() { |
||||
int uid = getuid(); |
||||
|
||||
// root is privileged
|
||||
if (!uid) |
||||
return 1; |
||||
|
||||
if (privileged_group) { |
||||
struct group *privgrp = getgrnam(privileged_group); |
||||
int pgid; |
||||
gid_t grps[128]; |
||||
int count, i; |
||||
if (!privgrp) |
||||
return 0; |
||||
pgid = privgrp->gr_gid; |
||||
|
||||
count = getgroups(sizeof(grps), grps); |
||||
for (i = 0; i < count; i++) |
||||
if (grps[i] == pgid) |
||||
return 1; |
||||
} |
||||
|
||||
return 0; |
||||
} |
||||
|
||||
char *ceo_get_user() { |
||||
struct passwd *pwent = getpwuid(getuid()); |
||||
if (pwent == NULL) |
||||
fatal("could not determine user"); |
||||
return xstrdup(pwent->pw_name); |
||||
} |
||||
|
||||
void ceo_notify_hook(int argc, ...) { |
||||
va_list args; |
||||
char **argv; |
||||
int i = 0; |
||||
|
||||
va_start(args, argc); |
||||
|
||||
argv = (char **)xmalloc(sizeof(char *) * (argc + 1)); |
||||
|
||||
while (i < argc) |
||||
argv[i++] = va_arg(args, char *); |
||||
|
||||
argv[i++] = NULL; |
||||
spawnv(notify_hook, argv); |
||||
|
||||
va_end(args); |
||||
} |
@ -0,0 +1,3 @@ |
||||
int ceo_get_privileged(); |
||||
char *ceo_get_user(); |
||||
void ceo_notify_hook(int, ...); |
@ -0,0 +1,21 @@ |
||||
#include <unistd.h> |
||||
#include <stdio.h> |
||||
#include <stdlib.h> |
||||
|
||||
#include "parser.h" |
||||
#include "util.h" |
||||
|
||||
void config_var(const char *name, const char *value) { |
||||
printf("%s = \"%s\"\n", name, value); |
||||
} |
||||
|
||||
int main(int argc, char *argv[]) { |
||||
if (argc < 2) { |
||||
fprintf(stderr, "usage: %s filename\n\n", argv[0]); |
||||
exit(1); |
||||
} |
||||
|
||||
config_parse(argv[1]); |
||||
|
||||
return 0; |
||||
} |
@ -0,0 +1,96 @@ |
||||
#include <stdlib.h> |
||||
#include <limits.h> |
||||
|
||||
#include "config.h" |
||||
#include "parser.h" |
||||
#include "util.h" |
||||
|
||||
#define DEF_STR NULL |
||||
#define DEF_LONG LONG_MIN |
||||
|
||||
char *server_url = DEF_STR; |
||||
|
||||
char *users_base = DEF_STR; |
||||
char *groups_base = DEF_STR; |
||||
|
||||
char *skeleton_dir = DEF_STR; |
||||
char *quota_prototype = DEF_STR; |
||||
char *homedir_prefix = DEF_STR; |
||||
|
||||
char *member_home = DEF_STR; |
||||
char *member_shell = DEF_STR; |
||||
long member_min_id = DEF_LONG; |
||||
long member_max_id = DEF_LONG; |
||||
|
||||
char *club_home = DEF_STR; |
||||
char *club_shell = DEF_STR; |
||||
long club_min_id = DEF_LONG; |
||||
long club_max_id = DEF_LONG; |
||||
|
||||
char *notify_hook = DEF_STR; |
||||
|
||||
char *realm = DEF_STR; |
||||
|
||||
char *admin_principal = DEF_STR; |
||||
char *admin_keytab = DEF_STR; |
||||
|
||||
char *admin_bind_userid = DEF_STR; |
||||
char *admin_bind_keytab = DEF_STR; |
||||
|
||||
char *sasl_realm = DEF_STR; |
||||
char *sasl_mech = DEF_STR; |
||||
|
||||
char *privileged_group = DEF_STR; |
||||
|
||||
long homedir_mode = DEF_LONG; |
||||
long homedir_min_uid = DEF_LONG; |
||||
|
||||
static char *strvarnames[] = { "server_url", "users_base", "admin_principal", |
||||
"admin_keytab", "skeleton_dir", "quota_prototype", "homedir_prefix", |
||||
"member_home", "member_shell", "club_home", "club_shell", "realm", |
||||
"admin_bind_userid", "admin_bind_keytab", "groups_base", |
||||
"privileged_group", "notify_hook", "sasl_realm", "sasl_mech" }; |
||||
static char **strvars[] = { &server_url, &users_base, &admin_principal, |
||||
&admin_keytab, &skeleton_dir, "a_prototype, &homedir_prefix, |
||||
&member_home, &member_shell, &club_home, &club_shell, &realm, |
||||
&admin_bind_userid, &admin_bind_keytab, &groups_base, |
||||
&privileged_group, ¬ify_hook, &sasl_realm, &sasl_mech }; |
||||
|
||||
static char *longvarnames[] = { "member_min_id", "member_max_id", |
||||
"homedir_mode", "homedir_min_uid", "club_min_id", "club_max_id" }; |
||||
static long *longvars[] = { &member_min_id, &member_max_id, &homedir_mode, |
||||
&homedir_min_uid, &club_min_id, &club_max_id }; |
||||
|
||||
void config_var(char *var, char *val) { |
||||
int i; |
||||
|
||||
for (i = 0; i < sizeof(strvars)/sizeof(*strvars); i++) { |
||||
if (!strcmp(var, strvarnames[i])) { |
||||
if (!strvars[i]) |
||||
free(strvars[i]); |
||||
*strvars[i] = xstrdup(val); |
||||
} |
||||
} |
||||
|
||||
for (i = 0; i < sizeof(longvars)/sizeof(*longvars); i++) { |
||||
if (!strcmp(var, longvarnames[i])) { |
||||
*longvars[i] = config_long(var, val); |
||||
} |
||||
} |
||||
} |
||||
|
||||
void configure() { |
||||
int i; |
||||
|
||||
config_parse(CONFIG_FILE); |
||||
|
||||
for (i = 0; i < sizeof(strvars)/sizeof(*strvars); i++) { |
||||
if (*strvars[i] == DEF_STR) |
||||
badconf("undefined string variable: %s", strvarnames[i]); |
||||
} |
||||
|
||||
for (i = 0; i < sizeof(longvars)/sizeof(*longvars); i++) { |
||||
if (*longvars[i] == DEF_LONG) |
||||
badconf("undefined long variable: %s", longvarnames[i]); |
||||
} |
||||
} |
@ -0,0 +1,42 @@ |
||||
#define CONFIG_FILE "/etc/csc/accounts.cf" |
||||
|
||||
extern char *server_url; |
||||
extern char *users_base; |
||||
extern char *groups_base; |
||||
|
||||
extern char *admin_bind_dn; |
||||
extern char *admin_bind_pw; |
||||
|
||||
extern char *skeleton_dir; |
||||
extern char *quota_prototype; |
||||
extern char *homedir_prefix; |
||||
|
||||
extern char *member_home; |
||||
extern char *member_shell; |
||||
extern long member_min_id; |
||||
extern long member_max_id; |
||||
|
||||
extern char *club_home; |
||||
extern char *club_shell; |
||||
extern long club_min_id; |
||||
extern long club_max_id; |
||||
|
||||
extern char *notify_hook; |
||||
|
||||
extern long homedir_mode; |
||||
extern long homedir_min_uid; |
||||
|
||||
extern char *realm; |
||||
|
||||
extern char *admin_principal; |
||||
extern char *admin_keytab; |
||||
|
||||
extern char *admin_bind_userid; |
||||
extern char *admin_bind_keytab; |
||||
|
||||
extern char *sasl_realm; |
||||
extern char *sasl_mech; |
||||
|
||||
extern char *privileged_group; |
||||
|
||||
void configure(); |
@ -0,0 +1,66 @@ |
||||
#include <kadm5/admin.h> |
||||
|
||||
#include "kadm.h" |
||||
#include "krb5.h" |
||||
#include "util.h" |
||||
#include "config.h" |
||||
|
||||
extern char *prog; |
||||
|
||||
static void *handle; |
||||
|
||||
void ceo_kadm_init() { |
||||
krb5_error_code retval; |
||||
kadm5_config_params params; |
||||
memset((void *) ¶ms, 0, sizeof(params)); |
||||
|
||||
retval = kadm5_init_with_skey(admin_principal, admin_keytab, |
||||
KADM5_ADMIN_SERVICE, ¶ms, KADM5_STRUCT_VERSION, |
||||
KADM5_API_VERSION_2, &handle); |
||||
if (retval) { |
||||
com_err(prog, retval, "while initializing kadm5"); |
||||
exit(1); |
||||
} |
||||
} |
||||
|
||||
void ceo_kadm_cleanup() { |
||||
kadm5_destroy(handle); |
||||
} |
||||
|
||||
int ceo_add_princ(char *user, char *password) { |
||||
krb5_error_code retval; |
||||
kadm5_principal_ent_rec princ; |
||||
memset((void *) &princ, 0, sizeof(princ)); |
||||
|
||||
if ((retval = krb5_parse_name(context, user, &princ.principal))) { |
||||
com_err(prog, retval, "while parsing principal name"); |
||||
return retval; |
||||
} |
||||
|
||||
if ((retval = kadm5_create_principal(handle, &princ, KADM5_PRINCIPAL, password))) { |
||||
com_err(prog, retval, "while creating principal"); |
||||
return retval; |
||||
} |
||||
|
||||
krb5_free_principal(context, princ.principal); |
||||
return 0; |
||||
} |
||||
|
||||
int ceo_del_princ(char *user) { |
||||
krb5_error_code retval; |
||||
krb5_principal princ; |
||||
|
||||
if ((retval = krb5_parse_name(context, user, &princ))) { |
||||
com_err(prog, retval, "while parsing principal name"); |
||||
return retval; |
||||
} |
||||
|
||||
retval = kadm5_delete_principal(handle, princ); |
||||
if (retval && retval != KADM5_UNK_PRINC) { |
||||
com_err(prog, retval, "while deleting principal"); |
||||
return retval; |
||||
} |
||||
|
||||
krb5_free_principal(context, princ); |
||||
return 0; |
||||
} |
@ -0,0 +1,5 @@ |
||||
void ceo_kadm_init(); |
||||
void ceo_kadm_cleanup(); |
||||
|
||||
int ceo_add_princ(char *, char *); |
||||
int ceo_del_princ(char *); |
@ -0,0 +1,149 @@ |
||||
#include <stdio.h> |
||||
#include <krb5.h> |
||||
#include <syslog.h> |
||||
|
||||
#include "krb5.h" |
||||
#include "util.h" |
||||
#include "config.h" |
||||
|
||||
extern char *prog; |
||||
|
||||
krb5_context context; |
||||
|
||||
static void com_err_hk(const char *whoami, long code, const char *fmt, va_list args) { |
||||
char message[4096]; |
||||
char *msgp = message; |
||||
|
||||
msgp += snprintf(msgp, sizeof(message) - 2 - (msgp - message), "%s ", error_message(code)); |
||||
if (msgp - message > sizeof(message) - 2) |
||||
fatal("error message overflowed"); |
||||
|
||||
msgp += vsnprintf(msgp, sizeof(message) - 2 - (msgp - message), fmt, args); |
||||
if (msgp - message > sizeof(message) - 2) |
||||
fatal("error message overflowed"); |
||||
|
||||
*msgp++ = '\n'; |
||||
*msgp++ = '\0'; |
||||
|
||||
syslog(LOG_ERR, "%s", message); |
||||
fprintf(stderr, "%s: %s", whoami, message); |
||||
} |
||||
|
||||
void ceo_krb5_init() { |
||||
krb5_error_code retval; |
||||
|
||||
set_com_err_hook(com_err_hk); |
||||
|
||||
retval = krb5_init_context(&context); |
||||
if (retval) { |
||||
com_err(prog, retval, "while initializing krb5"); |
||||
exit(1); |
||||
} |
||||
|
||||
retval = krb5_set_default_realm(context, realm); |
||||
if (retval) { |
||||
com_err(prog, retval, "while setting default realm"); |
||||
exit(1); |
||||
} |
||||
} |
||||
|
||||
void ceo_krb5_auth(char *principal, char *ktname) { |
||||
krb5_error_code retval; |
||||
krb5_creds creds; |
||||
krb5_principal princ; |
||||
krb5_keytab keytab; |
||||
krb5_ccache cache; |
||||
krb5_get_init_creds_opt options; |
||||
|
||||
krb5_get_init_creds_opt_init(&options); |
||||
memset(&creds, 0, sizeof(creds)); |
||||
|
||||
if ((retval = krb5_parse_name(context, principal, &princ))) { |
||||
com_err(prog, retval, "while resolving user %s", admin_bind_userid); |
||||
exit(1); |
||||
} |
||||
|
||||
if ((retval = krb5_cc_default(context, &cache))) { |
||||
com_err(prog, retval, "while resolving credentials cache"); |
||||
exit(1); |
||||
} |
||||
|
||||
if ((retval = krb5_kt_resolve(context, ktname, &keytab))) { |
||||
com_err(prog, retval, "while resolving keytab %s", admin_bind_keytab); |
||||
exit(1); |
||||
} |
||||
|
||||
if ((retval = krb5_get_init_creds_keytab(context, &creds, princ, keytab, 0, NULL, &options))) { |
||||
com_err(prog, retval, "while getting initial credentials"); |
||||
exit(1); |
||||
} |
||||
|
||||
if ((retval = krb5_cc_initialize(context, cache, princ))) { |
||||
com_err(prog, retval, "while initializing credentials cache"); |
||||
exit(1); |
||||
} |
||||
|
||||
if ((retval = krb5_cc_store_cred(context, cache, &creds))) { |
||||
com_err(prog, retval, "while storing credentials"); |
||||
exit(1); |
||||
} |
||||
|
||||
krb5_free_cred_contents(context, &creds); |
||||
krb5_kt_close(context, keytab); |
||||
krb5_free_principal(context, princ); |
||||
krb5_cc_close(context, cache); |
||||
} |
||||
|
||||
void ceo_krb5_deauth() { |
||||
krb5_error_code retval; |
||||
krb5_ccache cache; |
||||
|
||||
if ((retval = krb5_cc_default(context, &cache))) { |
||||
com_err(prog, retval, "while resolving credentials cache"); |
||||
exit(1); |
||||
} |
||||
|
||||
if ((retval = krb5_cc_destroy(context, cache))) { |
||||
com_err(prog, retval, "while destroying credentials cache"); |
||||
exit(1); |
||||
} |
||||
} |
||||
|
||||
void ceo_krb5_cleanup() { |
||||
krb5_free_context(context); |
||||
} |
||||
|
||||
int ceo_read_password(char *password, unsigned int size, int use_stdin) { |
||||
int tries = 0; |
||||
unsigned int len; |
||||
|
||||
do { |
||||
if (use_stdin) { |
||||
if (fgets(password, size, stdin) == NULL) |
||||
fatal("eof while reading password"); |
||||
|
||||
size = strlen(password); |
||||
|
||||
if (password[size - 1] == '\n') |
||||
password[size - 1] = '\0'; |
||||
} else { |
||||
len = size; |
||||
int retval = krb5_read_password(context, "New password", "Confirm password", password, &len); |
||||
if (retval == KRB5_LIBOS_PWDINTR) { |
||||
error("interrupted"); |
||||
return -1; |
||||
} else if (retval == KRB5_LIBOS_BADPWDMATCH) { |
||||
fputs("Passwords do not match.\n", stderr); |
||||
} else if (!password || !*password) { |
||||
fputs("Please enter a password.\n", stderr); |
||||
} |
||||
} |
||||
} while (++tries < 3 && !*password); |
||||
|
||||
if (!*password) { |
||||
error("maximum tries exceeded reading password"); |
||||
return -1; |
||||
} |
||||
|
||||
return 0; |
||||
} |
@ -0,0 +1,11 @@ |
||||
#include <krb5.h> |
||||
|
||||
extern krb5_context context; |
||||
|
||||
void ceo_krb5_init(); |
||||
void ceo_krb5_cleanup(); |
||||
|
||||
void ceo_krb5_auth(char *, char *); |
||||
void ceo_krb5_deauth(); |
||||
|
||||
int ceo_read_password(char *, unsigned int, int); |
@ -0,0 +1,286 @@ |
||||
#include <stdio.h> |
||||
#include <stdlib.h> |
||||
#include <pwd.h> |
||||
#include <grp.h> |
||||
#include <sasl/sasl.h> |
||||
#include <ldap.h> |
||||
#include <krb5.h> |
||||
|
||||
#include "ldap.h" |
||||
#include "krb5.h" |
||||
#include "config.h" |
||||
#include "util.h" |
||||
|
||||
extern char *prog; |
||||
|
||||
LDAP *ld; |
||||
|
||||
static void ldap_fatal(char *msg) { |
||||
int errnum; |
||||
char *errstr, *detail; |
||||
|
||||
ldap_get_option(ld, LDAP_OPT_ERROR_NUMBER, &errnum); |
||||
ldap_get_option(ld, LDAP_OPT_ERROR_STRING, &detail); |
||||
|
||||
errstr = ldap_err2string(errnum); |
||||
|
||||
if (detail && *detail) |
||||
fatal("%s: %s (%d): %s", msg, errstr, errnum, detail); |
||||
else |
||||
fatal("%s: %s (%d)", msg, errstr, errnum); |
||||
} |
||||
|
||||
static void ldap_err(char *msg) { |
||||
int errnum; |
||||
char *errstr, *detail; |
||||
|
||||
ldap_get_option(ld, LDAP_OPT_ERROR_NUMBER, &errnum); |
||||
ldap_get_option(ld, LDAP_OPT_ERROR_STRING, &detail); |
||||
|
||||
errstr = ldap_err2string(errnum); |
||||
|
||||
if (detail && *detail) |
||||
error("%s: %s (%d): %s", msg, errstr, errnum, detail); |
||||
else |
||||
error("%s: %s (%d)", msg, errstr, errnum); |
||||
} |
||||
|
||||
int ceo_add_group(char *cn, char *basedn, int no) { |
||||
if (!cn || !basedn) |
||||
fatal("addgroup: Invalid argument"); |
||||
|
||||
LDAPMod *mods[8]; |
||||
int i = -1; |
||||
int ret = 0; |
||||
|
||||
mods[++i] = xmalloc(sizeof(LDAPMod)); |
||||
mods[i]->mod_op = LDAP_MOD_ADD; |
||||
mods[i]->mod_type = "objectClass"; |
||||
char *objectClasses[] = { "top", "group", "posixGroup", NULL }; |
||||
mods[i]->mod_values = objectClasses; |
||||
|
||||
mods[++i] = xmalloc(sizeof(LDAPMod)); |
||||
mods[i]->mod_op = LDAP_MOD_ADD; |
||||
mods[i]->mod_type = "cn"; |
||||
char *uids[] = { cn, NULL }; |
||||
mods[i]->mod_values = uids; |
||||
|
||||
mods[++i] = xmalloc(sizeof(LDAPMod)); |
||||
mods[i]->mod_op = LDAP_MOD_ADD; |
||||
mods[i]->mod_type = "gidNumber"; |
||||
char idno[16]; |
||||
snprintf(idno, sizeof(idno), "%d", no); |
||||
char *gidNumbers[] = { idno, NULL }; |
||||
mods[i]->mod_values = gidNumbers; |
||||
|
||||
mods[++i] = NULL; |
||||
|
||||
char dn[1024]; |
||||
snprintf(dn, sizeof(dn), "cn=%s,%s", cn, basedn); |
||||
|
||||
if (ldap_add_s(ld, dn, mods) != LDAP_SUCCESS) { |
||||
ldap_err("addgroup"); |
||||
ret = -1; |
||||
} |
||||
|
||||
i = 0; |
||||
while (mods[i]) |
||||
free(mods[i++]); |
||||
|
||||
return ret; |
||||
} |
||||
|
||||
int ceo_add_user(char *uid, char *basedn, char *objclass, char *cn, char *home, char *shell, int no, ...) { |
||||
va_list args; |
||||
|
||||
if (!uid || !basedn || !cn || !home || !shell) |
||||
fatal("adduser: Invalid argument"); |
||||
|
||||
LDAPMod *mods[16]; |
||||
int i = -1; |
||||
int ret = 0; |
||||
|
||||
mods[++i] = xmalloc(sizeof(LDAPMod)); |
||||
mods[i]->mod_op = LDAP_MOD_ADD; |
||||
mods[i]->mod_type = "objectClass"; |
||||
char *objectClasses[] = { "top", "account", "posixAccount", "shadowAccount", NULL, NULL }; |
||||
if (objclass != NULL) |
||||
objectClasses[4] = objclass; |
||||
mods[i]->mod_values = objectClasses; |
||||
|
||||
mods[++i] = xmalloc(sizeof(LDAPMod)); |
||||
mods[i]->mod_op = LDAP_MOD_ADD; |
||||
mods[i]->mod_type = "uid"; |
||||
char *uids[] = { uid, NULL }; |
||||
mods[i]->mod_values = uids; |
||||
|
||||
mods[++i] = xmalloc(sizeof(LDAPMod)); |
||||
mods[i]->mod_op = LDAP_MOD_ADD; |
||||
mods[i]->mod_type = "cn"; |
||||
char *cns[] = { cn, NULL }; |
||||
mods[i]->mod_values = cns; |
||||
|
||||
mods[++i] = xmalloc(sizeof(LDAPMod)); |
||||
mods[i]->mod_op = LDAP_MOD_ADD; |
||||
mods[i]->mod_type = "uidNumber"; |
||||
char idno[16]; |
||||
snprintf(idno, sizeof(idno), "%d", no); |
||||
char *uidNumbers[] = { idno, NULL }; |
||||
mods[i]->mod_values = uidNumbers; |
||||
|
||||
mods[++i] = xmalloc(sizeof(LDAPMod)); |
||||
mods[i]->mod_op = LDAP_MOD_ADD; |
||||
mods[i]->mod_type = "gidNumber"; |
||||
mods[i]->mod_values = uidNumbers; |
||||
|
||||
mods[++i] = xmalloc(sizeof(LDAPMod)); |
||||
mods[i]->mod_op = LDAP_MOD_ADD; |
||||
mods[i]->mod_type = "homeDirectory"; |
||||
char *homeDirectory[] = { home, NULL }; |
||||
mods[i]->mod_values = homeDirectory; |
||||
|
||||
va_start(args, no); |
||||
char *attr; |
||||
while ((attr = va_arg(args, char *))) { |
||||
char *val = va_arg(args, char *); |
||||
|
||||
if (!val || !*val) |
||||
continue; |
||||
|
||||
if (i == sizeof(mods) / sizeof(*mods) - 2) { |
||||
error("too many attributes"); |
||||
return -1; |
||||
} |
||||
|
||||
mods[++i] = xmalloc(sizeof(LDAPMod)); |
||||
mods[i]->mod_op = LDAP_MOD_ADD; |
||||
mods[i]->mod_type = attr; |
||||
char *vals[] = { val, NULL }; |
||||
mods[i]->mod_values = vals; |
||||
} |
||||
|
||||
mods[++i] = NULL; |
||||
|
||||
char dn[1024]; |
||||
snprintf(dn, sizeof(dn), "uid=%s,%s", uid, basedn); |
||||
|
||||
if (ldap_add_s(ld, dn, mods) != LDAP_SUCCESS) { |
||||
ldap_err("adduser"); |
||||
ret = -1; |
||||
} |
||||
|
||||
i = 0; |
||||
while (mods[i]) |
||||
free(mods[i++]); |
||||
|
||||
return ret; |
||||
} |
||||
|
||||
int ceo_new_uid(int min, int max) { |
||||
char filter[64]; |
||||
char *attrs[] = { LDAP_NO_ATTRS, NULL }; |
||||
LDAPMessage *res; |
||||
int i; |
||||
|
||||
for (i = min; i <= max; i++) { |
||||
// id taken due to passwd
|
||||
if (getpwuid(i) != NULL) |
||||
continue; |
||||
|
||||
// id taken due to group
|
||||
if (getgrgid(i) != NULL) |
||||
continue; |
||||
|
||||
snprintf(filter, sizeof(filter), "(|(uidNumber=%d)(gidNumber=%d))", i, i); |
||||
if (ldap_search_s(ld, users_base, LDAP_SCOPE_SUBTREE, filter, attrs, 1, &res) != LDAP_SUCCESS) { |
||||
ldap_err("firstuid"); |
||||
return -1; |
||||
} |
||||
|
||||
int count = ldap_count_entries(ld, res); |
||||
ldap_msgfree(res); |
||||
|
||||
// id taken due to LDAP
|
||||
if (count) |
||||
continue; |
||||
|
||||
return i; |
||||
} |
||||
|
||||
return -1; |
||||
} |
||||
|
||||
int ceo_add_club(char *uid, char *cn) { |
||||
int id = ceo_new_uid(club_min_id, club_max_id); |
||||
|
||||
if (ceo_add_user(uid, users_base, "club", cn, club_home, club_shell, id, NULL)) |
||||
return -1; |
||||
|
||||
if (ceo_add_group(uid, groups_base, id)) |
||||
return -1; |
||||
|
||||
return 0; |
||||
} |
||||
|
||||
int ceo_user_exists(char *uid) { |
||||
char *attrs[] = { LDAP_NO_ATTRS, NULL }; |
||||
LDAPMessage *msg = NULL; |
||||
char filter[128]; |
||||
int count; |
||||
|
||||
if (!uid) |
||||
fatal("null uid"); |
||||
|
||||
snprintf(filter, sizeof(filter), "uid=%s", uid); |
||||
|
||||
if (ldap_search_s(ld, users_base, LDAP_SCOPE_SUBTREE, filter, attrs, 0, &msg) != LDAP_SUCCESS) { |
||||
ldap_err("user_exists"); |
||||
return -1; |
||||
} |
||||
|
||||
count = ldap_count_entries(ld, msg); |
||||
ldap_msgfree(msg); |
||||
|
||||
return count > 0; |
||||
} |
||||
|
||||
static int ldap_sasl_interact(LDAP *ld, unsigned flags, void *defaults, void *in) { |
||||
sasl_interact_t *interact = in; |
||||
|
||||
while (interact->id != SASL_CB_LIST_END) { |
||||
switch (interact->id) { |
||||
|
||||
// GSSAPI doesn't require any callbacks
|
||||
|
||||
default: |
||||
interact->result = ""; |
||||
interact->len = 0; |
||||
} |
||||
|
||||
interact++; |
||||
} |
||||
|
||||
return LDAP_SUCCESS; |
||||
} |
||||
|
||||
void ceo_ldap_init() { |
||||
int proto = LDAP_DEFAULT_PROTOCOL; |
||||
|
||||
if (ldap_initialize(&ld, server_url) != LDAP_SUCCESS) |
||||
ldap_fatal("ldap_initialize"); |
||||
|
||||
if (ldap_set_option(ld, LDAP_OPT_PROTOCOL_VERSION, &proto) != LDAP_OPT_SUCCESS) |
||||
ldap_fatal("ldap_set_option"); |
||||
|
||||
ceo_krb5_auth(admin_bind_userid, admin_bind_keytab); |
||||
|
||||
if (ldap_sasl_interactive_bind_s(ld, NULL, sasl_mech, NULL, NULL, |
||||
LDAP_SASL_QUIET, &ldap_sasl_interact, NULL) != LDAP_SUCCESS) |
||||
ldap_fatal("Bind failed"); |
||||
|
||||
ceo_krb5_deauth(); |
||||
} |
||||
|
||||
void ceo_ldap_cleanup() { |
||||
ldap_unbind(ld); |
||||
} |
@ -0,0 +1,10 @@ |
||||
#define LDAP_DEFAULT_PROTOCOL LDAP_VERSION3 |
||||
|
||||
int ceo_add_user(char *, char *, char *, char *, char *, char *, int, ...); |
||||
int ceo_add_group(char *, char *, int); |
||||
int ceo_new_uid(int, int); |
||||
|
||||
void ceo_ldap_init(); |
||||
void ceo_ldap_cleanup(); |
||||
|
||||
int ceo_user_exists(char *); |
@ -0,0 +1,214 @@ |
||||
#include <stdio.h> |
||||
#include <stdlib.h> |
||||
#include <errno.h> |
||||
#include <ctype.h> |
||||
#include <string.h> |
||||
#include <limits.h> |
||||
|
||||
#include "parser.h" |
||||
#include "util.h" |
||||
#include "config.h" |
||||
|
||||
#define VAR_MAX 256 |
||||
|
||||
void config_var(const char *, const char *); |
||||
|
||||
struct config_file { |
||||
FILE *p; |
||||
char *name; |
||||
int line; |
||||
struct config_file *parent; |
||||
int comment; |
||||
}; |
||||
|
||||
static void parse_config_file(char *, struct config_file *); |
||||
static void parse_error(struct config_file *file, char *msg) { |
||||
fatal("parse error on line %d of %s: %s", file->line, file->name, msg); |
||||
} |
||||
|
||||
static int parse_char(struct config_file *file) { |
||||
int c = getc(file->p); |
||||
if (c == '\n') |
||||
(file->line)++; |
||||
return c; |
||||
} |
||||
|
||||
static void unparse_char(struct config_file *file, int c) { |
||||
if (c == EOF) |
||||
return; |
||||
ungetc(c, file->p); |
||||
if (c == '\n') |
||||
(file->line)--; |
||||
} |
||||
|
||||
static void parse_name(struct config_file *file, char *name, size_t maxlen) { |
||||
int len = 0; |
||||
int c; |
||||
|
||||
for (;;) { |
||||
c = parse_char(file); |
||||
|
||||
if (c == EOF || c == '\n') { |
||||
unparse_char(file, c); |
||||
break; |
||||
} |
||||
|
||||
if (!isalpha(c) && c != '_' && c != '-') { |
||||
unparse_char(file, c); |
||||
break; |
||||
} |
||||
|
||||
if (len == maxlen - 1) |
||||
parse_error(file, "max name length exceeded"); |
||||
|
||||
name[len++] = c; |
||||
} |
||||
|
||||
if (len == 0) |
||||
parse_error(file, "expected name"); |
||||
|
||||
name[len++] = '\0'; |
||||
} |
||||
|
||||
static void parse_value(struct config_file *file, char *value, size_t maxlen) { |
||||
int len = 0; |
||||
int quote = 0; |
||||
int comment = 0; |
||||
int space = 0; |
||||
int c; |
||||
|
||||
for (;;) { |
||||
c = parse_char(file); |
||||
|
||||
if (c = |