43bd6877b3afc1b186cf68f53810403c28dfadd0
[debian/lxdm.git] / src / pam.c
1 /*
2 * lxdm.c - main entry of lxdm
3 *
4 * Copyright 2009 dgod <dgod.osa@gmail.com>
5 *
6 * This program is free software; you can redistribute it and/or modify
7 * it under the terms of the GNU General Public License as published by
8 * the Free Software Foundation; either version 3 of the License, or
9 * (at your option) any later version.
10 *
11 * This program is distributed in the hope that it will be useful,
12 * but WITHOUT ANY WARRANTY; without even the implied warranty of
13 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
14 * GNU General Public License for more details.
15 *
16 * You should have received a copy of the GNU General Public License
17 * along with this program; if not, write to the Free Software
18 * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston,
19 * MA 02110-1301, USA.
20 */
21
22 #define _GNU_SOURCE
23
24 #ifdef HAVE_CONFIG_H
25 #include <config.h>
26 #endif
27 #ifndef HAVE_LIBPAM
28 #ifdef USE_PAM
29 #define HAVE_LIBPAM 1
30 #else
31 #define HAVE_LIBPAM 0
32 #endif
33 #endif
34
35 #include <stdio.h>
36 #include <stdlib.h>
37 #include <string.h>
38 #include <stdarg.h>
39 #include <unistd.h>
40 #include <fcntl.h>
41 #include <dirent.h>
42 #include <errno.h>
43 #include <poll.h>
44 #include <sys/stat.h>
45
46 #include <pwd.h>
47 #include <grp.h>
48 #include <shadow.h>
49
50 #include <glib.h>
51
52 #include "lxdm.h"
53 #include "auth.h"
54
55 static void passwd_copy(struct passwd *dst,struct passwd *src)
56 {
57 dst->pw_name=g_strdup(src->pw_name);
58 dst->pw_uid=src->pw_uid;
59 dst->pw_gid=src->pw_gid;
60 if(src->pw_gecos)
61 dst->pw_gecos=g_strdup(src->pw_gecos);
62 dst->pw_dir=g_strdup(src->pw_dir);
63 dst->pw_shell=g_strdup(src->pw_shell);
64 }
65
66 static void passwd_clean(struct passwd *pw)
67 {
68 g_free(pw->pw_name);
69 g_free(pw->pw_gecos);
70 g_free(pw->pw_dir);
71 g_free(pw->pw_shell);
72 memset(pw,0,sizeof(*pw));
73 }
74
75 #if !HAVE_LIBPAM
76
77 int lxdm_auth_init(LXDM_AUTH *a)
78 {
79 memset(a,0,sizeof(*a));
80 return 0;
81 }
82
83 int lxdm_auth_cleanup(LXDM_AUTH *a)
84 {
85 passwd_clean(&a->pw);
86 return 0;
87 }
88
89 int lxdm_auth_user_authenticate(LXDM_AUTH *a,const char *user,const char *pass,int type)
90 {
91 struct passwd *pw;
92 struct spwd *sp;
93 char *real;
94 char *enc;
95 if(!user || !user[0])
96 {
97 g_debug("user==NULL\n");
98 return AUTH_ERROR;
99 }
100 pw = getpwnam(user);
101 endpwent();
102 if(!pw)
103 {
104 g_debug("user %s not found\n",user);
105 return AUTH_BAD_USER;
106 }
107 if(strstr(pw->pw_shell, "nologin"))
108 {
109 g_debug("user %s have nologin shell\n",user);
110 return AUTH_PRIV;
111 }
112 if(type==AUTH_TYPE_AUTO_LOGIN && !pass)
113 {
114 goto out;
115 }
116 sp = getspnam(user);
117 if( !sp )
118 {
119 return AUTH_FAIL;
120 }
121 endspent();
122 real = sp->sp_pwdp;
123 if( !real || !real[0] )
124 {
125 if( !pass || !pass[0] )
126 {
127 passwd_copy(&a->pw,pw);
128 g_debug("user %s auth with no password ok\n",user);
129 return AUTH_SUCCESS;
130 }
131 else
132 {
133 g_debug("user %s password not match\n",user);
134 return AUTH_FAIL;
135 }
136 }
137 enc = crypt(pass, real);
138 if( strcmp(real, enc) )
139 {
140 g_debug("user %s password not match\n",user);
141 return AUTH_FAIL;
142 }
143 out:
144 g_debug("user %s auth ok\n",pw->pw_name);
145 passwd_copy(&a->pw,pw);
146 return AUTH_SUCCESS;
147 }
148
149 int lxdm_auth_session_begin(LXDM_AUTH *a,const char *name,int tty,int display,char mcookie[16])
150 {
151 return 0;
152 }
153
154 int lxdm_auth_session_end(LXDM_AUTH *a)
155 {
156 return 0;
157 }
158
159 int lxdm_auth_clean_for_child(LXDM_AUTH *a)
160 {
161 return 0;
162 }
163
164 void lxdm_auth_print_env(LXDM_AUTH *a)
165 {
166 }
167
168 void lxdm_auth_put_env(LXDM_AUTH *a)
169 {
170 }
171
172 #else
173
174 #include <security/pam_appl.h>
175
176 static char *user_pass[2];
177
178 static int do_conv(int num, const struct pam_message **msg,struct pam_response **resp, void *arg)
179 {
180 int result = PAM_SUCCESS;
181 int i;
182 *resp = (struct pam_response *) calloc(num, sizeof(struct pam_response));
183 for(i=0;i<num;i++)
184 {
185 //printf("MSG: %d %s\n",msg[i]->msg_style,msg[i]->msg);
186 switch(msg[i]->msg_style){
187 case PAM_PROMPT_ECHO_ON:
188 resp[i]->resp=strdup(user_pass[0]?user_pass[0]:"");
189 break;
190 case PAM_PROMPT_ECHO_OFF:
191 //resp[i]->resp=strdup(user_pass[1]?user_pass[1]:"");
192 resp[i]->resp=user_pass[1]?strdup(user_pass[1]):NULL;
193 break;
194 case PAM_ERROR_MSG:
195 case PAM_TEXT_INFO:
196 //printf("PAM: %s\n",msg[i]->msg);
197 break;
198 default:
199 break;
200 }
201 }
202 return result;
203 }
204
205 static struct pam_conv conv={.conv=do_conv,.appdata_ptr=user_pass};
206
207 int lxdm_auth_init(LXDM_AUTH *a)
208 {
209 memset(a,0,sizeof(*a));
210 return 0;
211 }
212
213 int lxdm_auth_cleanup(LXDM_AUTH *a)
214 {
215 passwd_clean(&a->pw);
216 return 0;
217 }
218
219 int lxdm_auth_user_authenticate(LXDM_AUTH *a,const char *user,const char *pass,int type)
220 {
221 struct passwd *pw;
222 if(!user || !user[0])
223 {
224 g_debug("user==NULL\n");
225 return AUTH_ERROR;
226 }
227 pw = getpwnam(user);
228 endpwent();
229 if(!pw)
230 {
231 g_debug("user %s not found\n",user);
232 return AUTH_BAD_USER;
233 }
234 if(strstr(pw->pw_shell, "nologin"))
235 {
236 g_debug("user %s have nologin shell\n",user);
237 return AUTH_PRIV;
238 }
239 if(a->handle) pam_end(a->handle,0);
240 if(PAM_SUCCESS != pam_start("lxdm", pw->pw_name, &conv, (pam_handle_t**)&a->handle))
241 {
242 a->handle=NULL;
243 g_debug("user %s start pam fail\n",user);
244 return AUTH_FAIL;
245 }
246 else
247 {
248 int ret;
249 if(type==AUTH_TYPE_AUTO_LOGIN && !pass)
250 goto out;
251 user_pass[0]=(char*)user;user_pass[1]=(char*)pass;
252 ret=pam_authenticate(a->handle,PAM_SILENT);
253 user_pass[0]=0;user_pass[1]=0;
254 if(ret!=PAM_SUCCESS)
255 {
256 g_debug("user %s auth fail with %d\n",user,ret);
257 return AUTH_FAIL;
258 }
259 ret=pam_acct_mgmt(a->handle,PAM_SILENT);
260 if(ret!=PAM_SUCCESS)
261 {
262 g_debug("user %s acct mgmt fail with %d\n",user,ret);
263 return AUTH_FAIL;
264 }
265 }
266 out:
267 passwd_copy(&a->pw,pw);
268 return AUTH_SUCCESS;
269 }
270
271 int lxdm_auth_session_begin(LXDM_AUTH *a,const char *name,int tty,int display,char mcookie[16])
272 {
273 int err;
274 char x[256];
275
276 if(!a->handle)
277 {
278 return -1;
279 }
280 sprintf(x, "tty%d", tty);
281 pam_set_item(a->handle, PAM_TTY, x);
282 #ifdef PAM_XDISPLAY
283 sprintf(x,":%d",display);
284 pam_set_item(a->handle, PAM_XDISPLAY, x);
285 #endif
286 #if !defined(DISABLE_XAUTH) && defined(PAM_XAUTHDATA)
287 struct pam_xauth_data value;
288 value.name="MIT-MAGIC-COOKIE-1";
289 value.namelen=18;
290 value.data=mcookie;
291 value.datalen=16;
292 pam_set_item (a->handle, PAM_XAUTHDATA, &value);
293 #endif
294 if(name && name[0])
295 {
296 char *env;
297 env = g_strdup_printf ("DESKTOP_SESSION=%s", name);
298 pam_putenv (a->handle, env);
299 g_free (env);
300 }
301 err = pam_open_session(a->handle, 0); /* FIXME pam session failed */
302 if( err != PAM_SUCCESS )
303 g_warning( "pam open session error \"%s\"\n", pam_strerror(a->handle, err));
304 else
305 a->in_session=1;
306 return 0;
307 }
308
309 int lxdm_auth_session_end(LXDM_AUTH *a)
310 {
311 int err;
312 if(!a->handle)
313 return 0;
314 if(a->in_session)
315 {
316 err = pam_close_session(a->handle, 0);
317 a->in_session=0;
318 }
319 pam_end(a->handle, err);
320 a->handle = NULL;
321 passwd_clean(&a->pw);
322 return 0;
323 }
324
325 int lxdm_auth_clean_for_child(LXDM_AUTH *a)
326 {
327 pam_end(a->handle,0);
328 return 0;
329 }
330
331 void lxdm_auth_print_env(LXDM_AUTH *a)
332 {
333 int i;
334 char **penv;
335 if(!a->handle) return;
336 penv=pam_getenvlist(a->handle);
337 if(!penv) return;
338 for(i=0;penv[i]!=NULL;i++)
339 {
340 if(i!=0) printf(" ");
341 printf("%s",penv[i]);
342 }
343 free(penv);
344 }
345
346 void lxdm_auth_put_env(LXDM_AUTH *a)
347 {
348 int i;
349 char **penv;
350
351 if(!a->handle) return;
352 penv=pam_getenvlist(a->handle);
353 if(!penv) return;
354 for(i=0;penv[i]!=NULL;i++)
355 {
356 if(i!=0) printf(" ");
357 if(0!=putenv(penv[i]))
358 perror("putenv");
359 }
360 free(penv);
361 }
362
363 #endif
364
365 static void close_left_fds(void)
366 {
367 struct dirent **list;
368 char path[256];
369 int n;
370
371 snprintf(path,sizeof(path),"/proc/%d/fd",getpid());
372 n=scandir(path,&list,0,0);
373 if(n<0) return;
374 while(n--)
375 {
376 int fd=atoi(list[n]->d_name);
377 free(list[n]);
378 if(fd<=STDERR_FILENO)
379 continue;
380 close(fd);
381 }
382 free(list);
383
384 int fd = open("/dev/null", O_WRONLY);
385 if(fd == -1) return;
386 dup2(fd, 1);
387 dup2(fd, 2);
388 close(fd);
389 }
390
391 void switch_user(struct passwd *pw, const char *run, char **env)
392 {
393 int fd;
394
395 setenv("USER",pw->pw_name,1);
396 setenv("LOGNAME",pw->pw_name,1);
397 setenv("SHELL",pw->pw_shell,1);
398 setenv("HOME",pw->pw_dir,1);
399
400 g_spawn_command_line_sync ("/etc/lxdm/PreLogin",NULL,NULL,NULL,NULL);
401
402 if( !pw || initgroups(pw->pw_name, pw->pw_gid) ||
403 setgid(pw->pw_gid) || setuid(pw->pw_uid)/* || setsid() == -1 */)
404 exit(EXIT_FAILURE);
405 chdir(pw->pw_dir);
406 fd=open(".xsession-errors",O_WRONLY|O_CREAT|O_TRUNC,S_IRUSR|S_IWUSR);
407 if(fd!=-1)
408 {
409 dup2(fd,STDERR_FILENO);
410 close(fd);
411 }
412
413 /* reset signal */
414 signal(SIGCHLD, SIG_DFL);
415 signal(SIGTERM, SIG_DFL);
416 signal(SIGPIPE, SIG_DFL);
417 signal(SIGALRM, SIG_DFL);
418 signal(SIGHUP, SIG_DFL);
419 close_left_fds();
420
421 g_spawn_command_line_async ("/etc/lxdm/PostLogin",NULL);
422 execl("/etc/lxdm/Xsession","/etc/lxdm/Xsession",run,NULL);
423 perror("execle");
424 exit(EXIT_FAILURE);
425 }
426
427 void run_session(LXDM_AUTH *a,const char *run)
428 {
429 setsid();
430 a->child=fork();
431 if(a->child==0)
432 {
433 lxdm_auth_put_env(a);
434 lxdm_auth_clean_for_child(a);
435 switch_user(&a->pw,run,NULL);
436 //execle(run,run,NULL,environ);
437 _exit(EXIT_FAILURE);
438 }
439 }
440
441 LXDM_AUTH a;
442 static int session_exit=0;
443
444 static int xreadline(int fd,char *buf,size_t size)
445 {
446 int i;
447 for(i=0;i<size-1;i++)
448 {
449 int ret;
450 do{
451 ret=read(fd,buf+i,1);
452 }while(ret==-1 && errno==EINTR);
453 if(buf[i]==-1 || buf[i]=='\n')
454 break;
455 }
456 buf[i]=0;
457 return i;
458 }
459
460 int file_get_line(char *line, size_t n, FILE *fp)
461 {
462 int len;
463
464 if(session_exit)
465 return -1;
466 /*
467 if(!fgets(line,n,fp))
468 return -1;
469 len=strcspn(line,"\r\n");
470 line[len]=0;
471 */
472
473 struct pollfd fds;
474 fds.fd=fileno(fp);
475 fds.events=POLLIN;
476 poll(&fds,1,-1);
477 if(session_exit)
478 return -1;
479
480 len=xreadline(fileno(fp),line,n);
481 return len;
482 }
483
484 void sig_handler(int sig)
485 {
486 if(sig==SIGCHLD)
487 {
488 int wpid, status;
489 while(1)
490 {
491 wpid = waitpid(-1,&status,0);
492 if(wpid==a.child)
493 {
494 session_exit=1;
495 }
496 if(wpid<0) break;
497 }
498 }
499 }
500
501 int main(int arc,char *arg[])
502 {
503 char cmd[128];
504 int ret;
505
506 setvbuf(stdout, NULL, _IOLBF, 0 );
507 signal(SIGCHLD,sig_handler);
508
509 lxdm_auth_init(&a);
510 while(file_get_line(cmd,sizeof(cmd),stdin)>=0)
511 {
512 //fprintf(stderr,"begin %s\n",cmd);
513 if(!strcmp(cmd,"auth"))
514 {
515 char temp[8],user[64],pass[64];
516 int type;
517 ret=file_get_line(temp,sizeof(temp),stdin);
518 if(ret<0) break;
519 type=atoi(temp);
520 ret=file_get_line(user,sizeof(user),stdin);
521 if(ret<0) break;
522 if(type==AUTH_TYPE_NORMAL)
523 {
524 ret=file_get_line(pass,sizeof(pass),stdin);
525 if(ret<0) break;
526 ret=lxdm_auth_user_authenticate(&a,user,pass,type);
527 }
528 else
529 {
530 ret=lxdm_auth_user_authenticate(&a,user,NULL,type);
531 }
532 printf("%d\n",ret);
533 if(ret==AUTH_SUCCESS)
534 {
535 printf("%d\n",a.pw.pw_uid);
536 printf("%d\n",a.pw.pw_gid);
537 printf("%s\n",a.pw.pw_gecos?:"");
538 printf("%s\n",a.pw.pw_dir);
539 printf("%s\n",a.pw.pw_shell);
540 }
541 }
542 else if(!strcmp(cmd,"begin"))
543 {
544 char name[128],tty[8],display[8],mcookie[32];
545 gsize out_len;
546 ret=file_get_line(name,sizeof(name),stdin);
547 if(ret<0) break;
548 ret=file_get_line(tty,sizeof(tty),stdin);
549 if(ret<0) break;
550 ret=file_get_line(display,sizeof(display),stdin);
551 if(ret<0) break;
552 ret=file_get_line(mcookie,sizeof(mcookie),stdin);
553 if(ret<0) break;
554 g_base64_decode_inplace(mcookie,&out_len);
555 ret=lxdm_auth_session_begin(&a,name,atoi(tty),atoi(display),mcookie);
556 printf("%d\n",ret);
557 }
558 else if(!strcmp(cmd,"end"))
559 {
560 ret=lxdm_auth_session_end(&a);
561 printf("%d\n",ret);
562 }
563 else if(!strcmp(cmd,"env"))
564 {
565 lxdm_auth_print_env(&a);
566 printf("\n");
567 }
568 else if(!strcmp(cmd,"putenv"))
569 {
570 char env[1024];
571 while(file_get_line(env,sizeof(env),stdin)>0)
572 {
573 putenv(strdup(env));
574 }
575 }
576 else if(!strcmp(cmd,"exec"))
577 {
578 char run[256];
579 if(file_get_line(run,sizeof(run),stdin)>0) {
580 // some pam module likely replace the SIGCHLD handler
581 signal(SIGCHLD,sig_handler);
582 run_session(&a,run);
583 }
584 }
585 else if(!strcmp(cmd,"exit"))
586 {
587 break;
588 }
589 //fprintf(stderr,"end\n");
590 }
591 lxdm_auth_session_end(&a);
592 lxdm_auth_cleanup(&a);
593 return 0;
594 }
595