Disable logging to stderr if it is not a tty
[mspang/pyceo.git] / src / addmember.c
1 #include <unistd.h>
2 #include <sys/types.h>
3 #include <sys/wait.h>
4 #include <stdio.h>
5 #include <stdlib.h>
6 #include <getopt.h>
7 #include <ctype.h>
8 #include <pwd.h>
9 #include <grp.h>
10 #include <errno.h>
11 #include <libgen.h>
12 #include <syslog.h>
13
14 #include "util.h"
15 #include "common.h"
16 #include "config.h"
17 #include "ldap.h"
18 #include "krb5.h"
19 #include "kadm.h"
20 #include "addhomedir.h"
21
22 char *prog = NULL;
23 char *user = NULL;
24 int privileged = 0;
25
26 static int force = 0;
27 static int no_notify = 0;
28
29 static int use_stdin = 0;
30
31 static char *name = NULL;
32 static char *userid = NULL;
33 static char *program = NULL;
34 static char password[1024];
35
36 static struct option opts[] = {
37     { "force", 0, NULL, 'f' },
38     { "no-notify", 0, NULL, 'q' },
39     { "stdin", 0, NULL, 's' },
40     { NULL, 0, NULL, '\0' },
41 };
42
43 static void usage() {
44     fprintf(stderr, "Usage: %s userid realname [program]\n", prog);
45     exit(2);
46 }
47
48 int addmember() {
49     int krb_ok, user_ok, group_ok, home_ok;
50     int id;
51     char homedir[1024];
52     char acl_s[1024] = {0};
53
54     logmsg("adding uid=%s cn=%s program=%s by %s", userid, name, program, user);
55
56     if (setreuid(0, 0))
57         fatalpe("setreuid");
58     if (setregid(0, 0))
59         fatalpe("setregid");
60
61     if (!force && getpwnam(userid) != NULL)
62         deny("user %s already exists", userid);
63
64     snprintf(homedir, sizeof(homedir), "%s/%s", member_home, userid);
65
66     if (ceo_read_password(password, sizeof(password), use_stdin))
67         return 1;
68
69     ceo_krb5_init();
70     ceo_ldap_init();
71     ceo_kadm_init();
72
73     if (ceo_user_exists(userid))
74         deny("user %s already exists in LDAP", userid);
75     if (ceo_group_exists(userid))
76         deny("group %s already exists in LDAP", userid);
77
78     if ((id = ceo_new_uid(member_min_id, member_max_id)) <= 0)
79         fatal("no available uids in range [%d, %d]", member_min_id, member_max_id);
80
81     if (*member_home_acl) {
82         snprintf(acl_s, sizeof(acl_s), member_home_acl, userid);
83     }
84
85     krb_ok = ceo_del_princ(userid);
86     krb_ok = krb_ok || ceo_add_princ(userid, password);
87     if (!krb_ok)
88         logmsg("successfully created principal for %s", userid);
89
90     user_ok = krb_ok || ceo_add_user(userid, users_base, "member", name, homedir,
91             member_shell, id, "program", program, NULL);
92     if (!user_ok)
93         logmsg("successfully created account for %s", userid);
94
95     group_ok = user_ok || ceo_add_group(userid, groups_base, id);
96     if (!group_ok)
97         logmsg("successfully created group for %s", userid);
98
99     home_ok = user_ok || ceo_create_home(homedir, refquota, id, id, homedir_mode, acl_s);
100     if (!home_ok)
101         logmsg("successfully created home directory for %s", userid);
102
103     logmsg("done uid=%s", userid);
104
105     if (!no_notify && !user_ok) {
106         int pid;
107         int hkp[2];
108         FILE *hkf;
109         int status;
110
111         if (pipe(hkp))
112             errorpe("pipe");
113
114         fflush(stdout);
115         fflush(stderr);
116
117         pid = fork();
118
119         if (!pid) {
120             fclose(stdout);
121             fclose(stderr);
122             close(hkp[1]);
123             dup2(hkp[0], 0);
124             exit(execl(notify_hook, notify_hook, prog, user, userid, name, program, NULL));
125         }
126
127         hkf = fdopen(hkp[1], "w");
128
129         if (group_ok)
130             fprintf(hkf, "failed to create group\n");
131         if (home_ok)
132             fprintf(hkf, "failed to create home directory\n");
133         if (!group_ok && !home_ok)
134             fprintf(hkf, "all failures went undetected\n");
135
136         fclose(hkf);
137
138         waitpid(pid, &status, 0);
139
140         if (WIFEXITED(status) && WEXITSTATUS(status))
141             logmsg("hook %s exited with status %d", notify_hook, WEXITSTATUS(status));
142         else if (WIFSIGNALED(status))
143             logmsg("hook %s killed by signal %d", notify_hook, WTERMSIG(status));
144     }
145
146     ceo_kadm_cleanup();
147     ceo_ldap_cleanup();
148     ceo_krb5_cleanup();
149
150     return krb_ok || user_ok || group_ok || home_ok;
151 }
152
153 int main(int argc, char *argv[]) {
154     int opt;
155
156     prog = basename(argv[0]);
157     init_log(prog, LOG_PID, LOG_AUTHPRIV);
158
159     configure();
160
161     user = ceo_get_user();
162     privileged = ceo_get_privileged();
163
164     while ((opt = getopt_long(argc, argv, "", opts, NULL)) != -1) {
165         switch (opt) {
166             case 's':
167                 use_stdin = 1;
168                 break;
169             case 'f':
170                 if (!privileged)
171                     deny("not privileged enough to force");
172                 force = 1;
173                 break;
174             case 'q':
175                 if (!privileged)
176                     deny("not privileged enough to suppress notifications");
177                 no_notify = 1;
178                 break;
179             case '?':
180                 usage();
181                 break;
182             default:
183                 fatal("error parsing arguments");
184         }
185     }
186
187     if (argc - optind != 2 && argc - optind != 3)
188         usage();
189
190     userid = argv[optind++];
191     name = argv[optind++];
192
193     if (argc - optind)
194         program = argv[optind++];
195
196     return addmember();
197 }