anndl: Fixed a couple of bugs.
[utils.git] / userinit2.c
CommitLineData
efb71cce 1/*
2 * userinit2 - Launch programs as user during boot (Kerberized v2)
3 * Copyright (C) 2006 Fredrik Tolf (fredrik@dolda2000.com)
4 *
5 * This program is free software; you can redistribute it and/or modify
6 * it under the terms of the GNU General Public License as published by
7 * the Free Software Foundation; either version 2 of the License, or
8 * (at your option) any later version.
9 *
10 * This program is distributed in the hope that it will be useful,
11 * but WITHOUT ANY WARRANTY; without even the implied warranty of
12 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
13 * GNU General Public License for more details.
14 *
15 * You should have received a copy of the GNU General Public License
16 * along with this program; if not, write to the Free Software
17 * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA
18*/
19
20#include <stdlib.h>
21#include <stdio.h>
22#include <errno.h>
23#include <unistd.h>
24#include <string.h>
25#include <krb5.h>
26#include <pwd.h>
27#include <grp.h>
28#include <dirent.h>
29#include <fcntl.h>
30#include <sys/stat.h>
31
32krb5_context ctx;
33static int credsfwd;
34static int credsrnw;
35
36static int parsetime(char *ts)
37{
38 char *p;
39 int unit;
40
41 p = ts + strlen(ts) - 1;
42 unit = 1;
43 if(*p == 'm')
44 unit = 60;
45 else if(*p == 'h')
46 unit = 3600;
47 else if(*p == 'd')
48 unit = 86400;
49 return(atoi(ts) * unit);
50}
51
52static void getcreds(struct passwd *pw, krb5_keytab kt, krb5_principal prn)
53{
54 int ret, fd;
55 krb5_get_init_creds_opt o;
56 krb5_creds c;
57 krb5_ccache cc;
58 char buf[1024], *ccnm, *fnm;
59
60 krb5_get_init_creds_opt_init(&o);
61 krb5_get_init_creds_opt_set_forwardable(&o, credsfwd);
62 krb5_get_init_creds_opt_set_renew_life(&o, credsrnw);
63 if((ret = krb5_get_init_creds_keytab(ctx, &c, prn, kt, 0, NULL, &o)) != 0) {
64 fprintf(stderr, "userinit2: could not get krb credentials: %s\n", error_message(ret));
65 exit(1);
66 }
67 ccnm = buf + sprintf(buf, "KRB5CCNAME=");
68 fnm = ccnm + sprintf(ccnm, "FILE:");
69 sprintf(fnm, "/tmp/krb5cc_ui_%i_XXXXXX", pw->pw_uid);
70 if((fd = mkstemp(fnm)) < 0) {
71 fprintf(stderr, "userinit2: could not ccache file: %s", strerror(errno));
72 exit(1);
73 }
74 close(fd);
75 if((ret = krb5_cc_resolve(ctx, ccnm, &cc)) != 0) {
76 fprintf(stderr, "userinit2: could not resolve ccache %s: %s", ccnm, error_message(ret));
77 unlink(fnm);
78 exit(1);
79 }
80 if((ret = krb5_cc_initialize(ctx, cc, prn)) != 0) {
81 fprintf(stderr, "userinit2: could not initialize ccache: %s", error_message(ret));
82 unlink(fnm);
83 exit(1);
84 }
85 if((ret = krb5_cc_store_cred(ctx, cc, &c)) != 0) {
86 fprintf(stderr, "userinit2: could not store TGT: %s", error_message(ret));
87 unlink(fnm);
88 exit(1);
89 }
90 putenv(strdup(buf));
91 krb5_cc_close(ctx, cc);
92 krb5_free_cred_contents(ctx, &c);
93 if(chown(fnm, pw->pw_uid, pw->pw_gid)) {
94 fprintf(stderr, "userinit2: could not chown ccache file: %s", strerror(errno));
95 unlink(fnm);
96 exit(1);
97 }
98}
99
100static void dologin(struct passwd *pw)
101{
102 char ebuf[1024];
103
104 if(chdir(pw->pw_dir)) {
105 fprintf(stderr, "userinit2: could not chdir to home directory %s: %s\n", pw->pw_dir, strerror(errno));
106 exit(1);
107 }
108 if(snprintf(ebuf, sizeof(ebuf), "HOME=%s", pw->pw_dir) < sizeof(ebuf))
109 putenv(strdup(ebuf));
110 if(snprintf(ebuf, sizeof(ebuf), "SHELL=%s", pw->pw_shell) < sizeof(ebuf))
111 putenv(strdup(ebuf));
112 if(snprintf(ebuf, sizeof(ebuf), "USER=%s", pw->pw_name) < sizeof(ebuf))
113 putenv(strdup(ebuf));
114 if(snprintf(ebuf, sizeof(ebuf), "LOGNAME=%s", pw->pw_name) < sizeof(ebuf))
115 putenv(strdup(ebuf));
116 if(snprintf(ebuf, sizeof(ebuf), "PATH=%s/bin:/usr/local/bin:/bin:/usr/bin", pw->pw_dir) < sizeof(ebuf))
117 putenv(strdup(ebuf));
118}
119
120static void dodir(void)
121{
122 DIR *dir;
123 struct dirent *de;
124 struct stat sb;
125
126 if((dir = opendir(".")) == NULL) {
127 fprintf(stderr, "userinit2: couldn't open cwd (%s)!\n", strerror(errno));
128 exit(1);
129 }
130 while((de = readdir(dir)) != NULL) {
131 if(de->d_name[0] == '.')
132 continue;
133 if(access(de->d_name, X_OK))
134 continue;
135 if(stat(de->d_name, &sb))
136 continue;
137 if(!S_ISREG(sb.st_mode))
138 continue;
139 if(!fork()) {
140 setpgrp();
141 execl(de->d_name, de->d_name, NULL);
142 exit(127);
143 }
144 }
145 closedir(dir);
146}
147
148static void runstuff(struct passwd *pw)
149{
150 int i, fd1, fd2;
151 char buf[1024];
152
153 if(chdir(".userinit"))
154 return;
155 for(i = 3; i < FD_SETSIZE; i++)
156 close(i);
157 if((fd1 = open("/dev/null", O_RDWR)) < 0) {
158 fprintf(stderr, "userinit2: /dev/null: %s\n", strerror(errno));
159 exit(1);
160 }
161 if((fd2 = open("stderr", O_WRONLY | O_CREAT | O_APPEND, 0666)) < 0) {
162 fprintf(stderr, "userinit2: stderr: %s\n", strerror(errno));
163 exit(1);
164 }
165 dup2(fd1, 0);
166 dup2(fd1, 1);
167 dup2(fd2, 2);
168 close(fd1);
169 close(fd2);
170 setsid();
171 dodir();
172 if(gethostname(buf, sizeof(buf)))
173 return;
174 if(chdir(buf))
175 return;
176 dodir();
177}
178
179int main(int argc, char **argv)
180{
181 int ret, c;
182 krb5_keytab kt;
183 krb5_kt_cursor ktc;
184 krb5_keytab_entry kte;
185 struct passwd *pw;
186
187 while((c = getopt(argc, argv, "hfr:")) >= 0) {
188 switch(c) {
189 case 'f':
190 credsfwd = 1;
191 break;
192 case 'r':
193 credsrnw = parsetime(optarg);
194 break;
195 default:
196 fprintf(stderr, "usage: userinit2 [-hf] [-r renewlife]\n");
197 exit((c == 'h')?0:1);
198 }
199 }
200 if((ret = krb5_init_context(&ctx)) != 0) {
201 fprintf(stderr, "userinit2: could not get krb context: %s\n", error_message(ret));
202 exit(1);
203 }
204 if((ret = krb5_kt_default(ctx, &kt)) != 0) {
205 fprintf(stderr, "userinit2: could not get keytab: %s\n", error_message(ret));
206 exit(1);
207 }
208 if((ret = krb5_kt_start_seq_get(ctx, kt, &ktc)) != 0) {
209 fprintf(stderr, "userinit2: could not iterate keytab: %s\n", error_message(ret));
210 exit(1);
211 }
212 while(krb5_kt_next_entry(ctx, kt, &kte, &ktc) == 0) {
213 do {
214 if((kte.principal->length >= 2) && !strcmp(kte.principal->data[1].data, "userinit")) {
215 if((pw = getpwnam(kte.principal->data[0].data)) == NULL)
216 break;
217 if(!(ret = fork())) {
218 getcreds(pw, kt, kte.principal);
219 if(getuid() == 0) {
220 if(initgroups(pw->pw_name, pw->pw_gid)) {
221 fprintf(stderr, "userinit2: initgroups: %s\n", strerror(errno));
222 exit(1);
223 }
224 if(setgid(pw->pw_gid)) {
225 fprintf(stderr, "userinit2: setgid: %s\n", strerror(errno));
226 exit(1);
227 }
228 if(setuid(pw->pw_uid)) {
229 fprintf(stderr, "userinit2: setuid: %s\n", strerror(errno));
230 exit(1);
231 }
232 } else {
233 if(pw->pw_uid != getuid())
234 break;
235 }
236 dologin(pw);
237 runstuff(pw);
238 exit(0);
239 }
240 if(ret < 0) {
241 fprintf(stderr, "userinit2: fork: %s\n", strerror(errno));
242 exit(1);
243 }
244 }
245 } while(0);
246 krb5_free_keytab_entry_contents(ctx, &kte);
247 }
248 krb5_kt_end_seq_get(ctx, kt, &ktc);
249 krb5_kt_close(ctx, kt);
250 return(0);
251}
252
253/*
254 * Local Variables:
255 * compile-command: "gcc -Wall -g -o userinit2 userinit2.c -lkrb5"
256 * End:
257 */