Merging upstream version 0.5.3 (Closes: #805659 CVE-2015-8308).
[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 #include <sys/wait.h>
46
47 #include <pwd.h>
48 #include <grp.h>
49 #include <shadow.h>
50
51 #include <glib.h>
52
53 #include "lxdm.h"
54 #include "auth.h"
55
56 static void passwd_copy(struct passwd *dst,struct passwd *src)
57 {
58 dst->pw_name=g_strdup(src->pw_name);
59 dst->pw_uid=src->pw_uid;
60 dst->pw_gid=src->pw_gid;
61 if(src->pw_gecos)
62 dst->pw_gecos=g_strdup(src->pw_gecos);
63 dst->pw_dir=g_strdup(src->pw_dir);
64 dst->pw_shell=g_strdup(src->pw_shell);
65 }
66
67 static void passwd_clean(struct passwd *pw)
68 {
69 g_free(pw->pw_name);
70 g_free(pw->pw_gecos);
71 g_free(pw->pw_dir);
72 g_free(pw->pw_shell);
73 memset(pw,0,sizeof(*pw));
74 }
75
76 #if !HAVE_LIBPAM
77
78 int lxdm_auth_init(LXDM_AUTH *a)
79 {
80 memset(a,0,sizeof(*a));
81 return 0;
82 }
83
84 int lxdm_auth_cleanup(LXDM_AUTH *a)
85 {
86 passwd_clean(&a->pw);
87 return 0;
88 }
89
90 int lxdm_auth_user_authenticate(LXDM_AUTH *a,const char *user,const char *pass,int type)
91 {
92 struct passwd *pw;
93 struct spwd *sp;
94 char *real;
95 char *enc;
96 if(!user || !user[0])
97 {
98 g_debug("user==NULL\n");
99 return AUTH_ERROR;
100 }
101 pw = getpwnam(user);
102 endpwent();
103 if(!pw)
104 {
105 g_debug("user %s not found\n",user);
106 return AUTH_BAD_USER;
107 }
108 if(strstr(pw->pw_shell, "nologin"))
109 {
110 g_debug("user %s have nologin shell\n",user);
111 return AUTH_PRIV;
112 }
113 if(type==AUTH_TYPE_AUTO_LOGIN && !pass)
114 {
115 goto out;
116 }
117 sp = getspnam(user);
118 if( !sp )
119 {
120 return AUTH_FAIL;
121 }
122 endspent();
123 real = sp->sp_pwdp;
124 if( !real || !real[0] )
125 {
126 if( !pass || !pass[0] )
127 {
128 passwd_copy(&a->pw,pw);
129 g_debug("user %s auth with no password ok\n",user);
130 return AUTH_SUCCESS;
131 }
132 else
133 {
134 g_debug("user %s password not match\n",user);
135 return AUTH_FAIL;
136 }
137 }
138 enc = crypt(pass, real);
139 if( strcmp(real, enc) )
140 {
141 g_debug("user %s password not match\n",user);
142 return AUTH_FAIL;
143 }
144 out:
145 g_debug("user %s auth ok\n",pw->pw_name);
146 passwd_copy(&a->pw,pw);
147 return AUTH_SUCCESS;
148 }
149
150 int lxdm_auth_session_begin(LXDM_AUTH *a,const char *name,int tty,int display,char mcookie[16])
151 {
152 return 0;
153 }
154
155 int lxdm_auth_session_end(LXDM_AUTH *a)
156 {
157 return 0;
158 }
159
160 int lxdm_auth_clean_for_child(LXDM_AUTH *a)
161 {
162 return 0;
163 }
164
165 void lxdm_auth_print_env(LXDM_AUTH *a)
166 {
167 }
168
169 void lxdm_auth_put_env(LXDM_AUTH *a)
170 {
171 }
172
173 #else
174
175 #include <security/pam_appl.h>
176
177 static char *user_pass[2];
178
179 static int do_conv(int num, const struct pam_message **msg,struct pam_response **resp, void *arg)
180 {
181 int result = PAM_SUCCESS;
182 int i;
183 *resp = (struct pam_response *) calloc(num, sizeof(struct pam_response));
184 for(i=0;i<num;i++)
185 {
186 //printf("MSG: %d %s\n",msg[i]->msg_style,msg[i]->msg);
187 switch(msg[i]->msg_style){
188 case PAM_PROMPT_ECHO_ON:
189 resp[i]->resp=strdup(user_pass[0]?user_pass[0]:"");
190 break;
191 case PAM_PROMPT_ECHO_OFF:
192 //resp[i]->resp=strdup(user_pass[1]?user_pass[1]:"");
193 resp[i]->resp=user_pass[1]?strdup(user_pass[1]):NULL;
194 break;
195 case PAM_ERROR_MSG:
196 case PAM_TEXT_INFO:
197 //printf("PAM: %s\n",msg[i]->msg);
198 break;
199 default:
200 break;
201 }
202 }
203 return result;
204 }
205
206 static struct pam_conv conv={.conv=do_conv,.appdata_ptr=user_pass};
207
208 int lxdm_auth_init(LXDM_AUTH *a)
209 {
210 memset(a,0,sizeof(*a));
211 return 0;
212 }
213
214 int lxdm_auth_cleanup(LXDM_AUTH *a)
215 {
216 passwd_clean(&a->pw);
217 return 0;
218 }
219
220 int lxdm_auth_user_authenticate(LXDM_AUTH *a,const char *user,const char *pass,int type)
221 {
222 struct passwd *pw;
223 if(!user || !user[0])
224 {
225 g_debug("user==NULL\n");
226 return AUTH_ERROR;
227 }
228 pw = getpwnam(user);
229 endpwent();
230 if(!pw)
231 {
232 g_debug("user %s not found\n",user);
233 return AUTH_BAD_USER;
234 }
235 if(strstr(pw->pw_shell, "nologin"))
236 {
237 g_debug("user %s have nologin shell\n",user);
238 return AUTH_PRIV;
239 }
240 if(a->handle) pam_end(a->handle,0);
241 if(PAM_SUCCESS != pam_start("lxdm", pw->pw_name, &conv, (pam_handle_t**)&a->handle))
242 {
243 a->handle=NULL;
244 g_debug("user %s start pam fail\n",user);
245 return AUTH_FAIL;
246 }
247 else
248 {
249 int ret;
250 if(type==AUTH_TYPE_AUTO_LOGIN && !pass)
251 goto out;
252 user_pass[0]=(char*)user;user_pass[1]=(char*)pass;
253 ret=pam_authenticate(a->handle,PAM_SILENT);
254 user_pass[0]=0;user_pass[1]=0;
255 if(ret!=PAM_SUCCESS)
256 {
257 g_debug("user %s auth fail with %d\n",user,ret);
258 return AUTH_FAIL;
259 }
260 ret=pam_acct_mgmt(a->handle,PAM_SILENT);
261 if(ret!=PAM_SUCCESS)
262 {
263 g_debug("user %s acct mgmt fail with %d\n",user,ret);
264 return AUTH_FAIL;
265 }
266 }
267 out:
268 passwd_copy(&a->pw,pw);
269 return AUTH_SUCCESS;
270 }
271
272 int lxdm_auth_session_begin(LXDM_AUTH *a,const char *name,int tty,int display,char mcookie[16])
273 {
274 int err;
275 char x[256];
276
277 if(!a->handle)
278 {
279 return -1;
280 }
281 sprintf(x, "tty%d", tty);
282 pam_set_item(a->handle, PAM_TTY, x);
283 #ifdef PAM_XDISPLAY
284 sprintf(x,":%d",display);
285 pam_set_item(a->handle, PAM_XDISPLAY, x);
286 #endif
287 #if !defined(DISABLE_XAUTH) && defined(PAM_XAUTHDATA)
288 struct pam_xauth_data value;
289 value.name="MIT-MAGIC-COOKIE-1";
290 value.namelen=18;
291 value.data=mcookie;
292 value.datalen=16;
293 pam_set_item (a->handle, PAM_XAUTHDATA, &value);
294 #endif
295 if(name && name[0])
296 {
297 char *env;
298 env = g_strdup_printf ("DESKTOP_SESSION=%s", name);
299 pam_putenv (a->handle, env);
300 g_free (env);
301 }
302 err = pam_open_session(a->handle, 0); /* FIXME pam session failed */
303 if( err != PAM_SUCCESS )
304 {
305 g_warning( "pam open session error \"%s\"\n", pam_strerror(a->handle, err));
306 }
307 else
308 {
309 a->in_session=1;
310 }
311 return 0;
312 }
313
314 static int proc_filter(const struct dirent *d)
315 {
316 int c=d->d_name[0];
317 return c>='1' && c<='9';
318 }
319
320 static int check_process_sid(int pid,const char *sid)
321 {
322 char path[128];
323 FILE *fp;
324 gchar *env_data,*p;
325 gsize env_len;
326 int res=0;
327
328 sprintf(path,"/proc/%d/environ",pid);
329 if(!g_file_get_contents(path,&env_data,&env_len,NULL))
330 {
331 return 0;
332 }
333 for(p=env_data;p!=NULL && p-env_data<env_len;)
334 {
335 if(!strncmp(p,"XDG_SESSION_ID=",15))
336 {
337 if(!strcmp(sid,p+15))
338 res=1;
339 break;
340 }
341 p=strchr(p,'\0');
342 if(!p) break;p++;
343 }
344 g_free(env_data);
345
346 return res;
347 }
348
349 static void kill_left_process(const char *sid)
350 {
351 int self=getpid();
352 struct dirent **list;
353 int i,n;
354
355 n=scandir("/proc",&list,proc_filter,0);
356 if(n<0) return;
357 for(i=0;i<n;i++)
358 {
359 int pid=atoi(list[i]->d_name);
360 if(pid==self || pid<=1)
361 continue;
362 if(check_process_sid(pid,sid))
363 {
364 kill(pid,SIGKILL);
365 }
366 }
367 free(list);
368 }
369
370 int lxdm_auth_session_end(LXDM_AUTH *a)
371 {
372 int err;
373 if(!a->handle)
374 return 0;
375 if(a->in_session)
376 {
377 char xdg_session_id[32]={0};
378 const char *p=pam_getenv(a->handle,"XDG_SESSION_ID");
379 if(p!=NULL) snprintf(xdg_session_id,32,"%s",p);
380 err = pam_close_session(a->handle, 0);
381 if( err != PAM_SUCCESS )
382 {
383 g_warning( "pam close session error \"%s\"\n", pam_strerror(a->handle, err));
384 }
385 a->in_session=0;
386 if(p!=NULL)
387 {
388 usleep(100*1000);
389 kill_left_process(xdg_session_id);
390 }
391 }
392 pam_end(a->handle, err);
393 a->handle = NULL;
394 passwd_clean(&a->pw);
395 return 0;
396 }
397
398 int lxdm_auth_clean_for_child(LXDM_AUTH *a)
399 {
400 pam_end(a->handle,0);
401 return 0;
402 }
403
404 void lxdm_auth_print_env(LXDM_AUTH *a)
405 {
406 int i;
407 char **penv;
408 if(!a->handle) return;
409 penv=pam_getenvlist(a->handle);
410 if(!penv) return;
411 for(i=0;penv[i]!=NULL;i++)
412 {
413 if(i!=0) printf(" ");
414 printf("%s",penv[i]);
415 }
416 free(penv);
417 }
418
419 void lxdm_auth_put_env(LXDM_AUTH *a)
420 {
421 int i;
422 char **penv;
423
424 if(!a->handle) return;
425 penv=pam_getenvlist(a->handle);
426 if(!penv) return;
427 for(i=0;penv[i]!=NULL;i++)
428 {
429 if(i!=0) printf(" ");
430 if(0!=putenv(penv[i]))
431 perror("putenv");
432 }
433 free(penv);
434 }
435
436 #endif
437
438 static void close_left_fds(void)
439 {
440 struct dirent **list;
441 char path[256];
442 int n;
443
444 snprintf(path,sizeof(path),"/proc/%d/fd",getpid());
445 n=scandir(path,&list,0,0);
446 if(n<0) return;
447 while(n--)
448 {
449 int fd=atoi(list[n]->d_name);
450 free(list[n]);
451 if(fd<=STDERR_FILENO)
452 continue;
453 close(fd);
454 }
455 free(list);
456
457 int fd = open("/dev/null", O_WRONLY);
458 if(fd == -1) return;
459 dup2(fd, 1);
460 dup2(fd, 2);
461 close(fd);
462 }
463
464 void switch_user(struct passwd *pw, const char *run, char **env)
465 {
466 int fd;
467
468 setenv("USER",pw->pw_name,1);
469 setenv("LOGNAME",pw->pw_name,1);
470 setenv("SHELL",pw->pw_shell,1);
471 setenv("HOME",pw->pw_dir,1);
472
473 g_spawn_command_line_sync ("/etc/lxdm/PreLogin",NULL,NULL,NULL,NULL);
474
475 if( !pw || initgroups(pw->pw_name, pw->pw_gid) ||
476 setgid(pw->pw_gid) || setuid(pw->pw_uid) || setsid()==-1)
477 {
478 exit(EXIT_FAILURE);
479 }
480 chdir(pw->pw_dir);
481 fd=open(".xsession-errors",O_WRONLY|O_CREAT|O_TRUNC,S_IRUSR|S_IWUSR);
482 if(fd!=-1)
483 {
484 dup2(fd,STDERR_FILENO);
485 close(fd);
486 }
487
488 /* reset signal */
489 signal(SIGCHLD, SIG_DFL);
490 signal(SIGTERM, SIG_DFL);
491 signal(SIGPIPE, SIG_DFL);
492 signal(SIGALRM, SIG_DFL);
493 signal(SIGHUP, SIG_DFL);
494 close_left_fds();
495
496 g_spawn_command_line_async ("/etc/lxdm/PostLogin",NULL);
497 execl("/etc/lxdm/Xsession","/etc/lxdm/Xsession",run,NULL);
498 perror("execle");
499 exit(EXIT_FAILURE);
500 }
501
502 void run_session(LXDM_AUTH *a,const char *run)
503 {
504 a->child=fork();
505 if(a->child==0)
506 {
507 lxdm_auth_put_env(a);
508 lxdm_auth_clean_for_child(a);
509 switch_user(&a->pw,run,NULL);
510 //execle(run,run,NULL,environ);
511 _exit(EXIT_FAILURE);
512 }
513 }
514
515 LXDM_AUTH a;
516 static int session_exit=0;
517
518 static int xreadline(int fd,char *buf,size_t size)
519 {
520 int i;
521 for(i=0;i<size-1;i++)
522 {
523 int ret;
524 do{
525 ret=read(fd,buf+i,1);
526 }while(ret==-1 && errno==EINTR);
527 if(buf[i]==-1 || buf[i]=='\n')
528 break;
529 }
530 buf[i]=0;
531 return i;
532 }
533
534 int file_get_line(char *line, size_t n, FILE *fp)
535 {
536 int len;
537
538 if(session_exit)
539 return -1;
540 /*
541 if(!fgets(line,n,fp))
542 return -1;
543 len=strcspn(line,"\r\n");
544 line[len]=0;
545 */
546
547 struct pollfd fds;
548 fds.fd=fileno(fp);
549 fds.events=POLLIN;
550 poll(&fds,1,-1);
551 if(session_exit)
552 return -1;
553
554 len=xreadline(fileno(fp),line,n);
555 return len;
556 }
557
558 void sig_handler(int sig)
559 {
560 if(sig==SIGCHLD)
561 {
562 int wpid, status;
563 while(1)
564 {
565 wpid = waitpid(-1,&status,0);
566 if(wpid==a.child)
567 {
568 session_exit=1;
569 }
570 if(wpid<0) break;
571 }
572 }
573 }
574
575 int main(int arc,char *arg[])
576 {
577 char cmd[128];
578 int ret;
579
580 setvbuf(stdout, NULL, _IOLBF, 0 );
581 signal(SIGCHLD,sig_handler);
582
583 lxdm_auth_init(&a);
584 while(file_get_line(cmd,sizeof(cmd),stdin)>=0)
585 {
586 //fprintf(stderr,"begin %s\n",cmd);
587 if(!strcmp(cmd,"auth"))
588 {
589 char temp[8],user[64],pass[64];
590 int type;
591 ret=file_get_line(temp,sizeof(temp),stdin);
592 if(ret<0) break;
593 type=atoi(temp);
594 ret=file_get_line(user,sizeof(user),stdin);
595 if(ret<0) break;
596 if(type==AUTH_TYPE_NORMAL)
597 {
598 ret=file_get_line(pass,sizeof(pass),stdin);
599 if(ret<0) break;
600 ret=lxdm_auth_user_authenticate(&a,user,pass,type);
601 }
602 else
603 {
604 ret=lxdm_auth_user_authenticate(&a,user,NULL,type);
605 }
606 printf("%d\n",ret);
607 if(ret==AUTH_SUCCESS)
608 {
609 printf("%d\n",a.pw.pw_uid);
610 printf("%d\n",a.pw.pw_gid);
611 printf("%s\n",a.pw.pw_gecos?:"");
612 printf("%s\n",a.pw.pw_dir);
613 printf("%s\n",a.pw.pw_shell);
614 }
615 }
616 else if(!strcmp(cmd,"begin"))
617 {
618 char name[128],tty[8],display[8],mcookie[32];
619 gsize out_len;
620 ret=file_get_line(name,sizeof(name),stdin);
621 if(ret<0) break;
622 ret=file_get_line(tty,sizeof(tty),stdin);
623 if(ret<0) break;
624 ret=file_get_line(display,sizeof(display),stdin);
625 if(ret<0) break;
626 ret=file_get_line(mcookie,sizeof(mcookie),stdin);
627 if(ret<0) break;
628 g_base64_decode_inplace(mcookie,&out_len);
629 ret=lxdm_auth_session_begin(&a,name,atoi(tty),atoi(display),mcookie);
630 printf("%d\n",ret);
631 }
632 else if(!strcmp(cmd,"end"))
633 {
634 ret=lxdm_auth_session_end(&a);
635 printf("%d\n",ret);
636 }
637 else if(!strcmp(cmd,"env"))
638 {
639 lxdm_auth_print_env(&a);
640 printf("\n");
641 }
642 else if(!strcmp(cmd,"putenv"))
643 {
644 char env[1024];
645 while(file_get_line(env,sizeof(env),stdin)>0)
646 {
647 putenv(strdup(env));
648 }
649 }
650 else if(!strcmp(cmd,"exec"))
651 {
652 char run[256];
653 if(file_get_line(run,sizeof(run),stdin)>0) {
654 // some pam module likely replace the SIGCHLD handler
655 signal(SIGCHLD,sig_handler);
656 run_session(&a,run);
657 }
658 }
659 else if(!strcmp(cmd,"exit"))
660 {
661 break;
662 }
663 //fprintf(stderr,"end\n");
664 }
665 lxdm_auth_session_end(&a);
666 lxdm_auth_cleanup(&a);
667 return 0;
668 }
669