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