Update title RE.
[utils.git] / userinit2.c
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
32 krb5_context ctx;
33 static int credsfwd;
34 static int credsrnw;
35
36 static 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
52 static 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
100 static 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
120 static 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
148 static 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
179 int 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  */