1 /* $Xorg: verify.c,v 1.4 2001/02/09 02:05:41 xorgcvs Exp $ */
2 /*
3
4 Copyright 1988, 1998 The Open Group
5
6 Permission to use, copy, modify, distribute, and sell this software and its
7 documentation for any purpose is hereby granted without fee, provided that
8 the above copyright notice appear in all copies and that both that
9 copyright notice and this permission notice appear in supporting
10 documentation.
11
12 The above copyright notice and this permission notice shall be included
13 in all copies or substantial portions of the Software.
14
15 THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS
16 OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF
17 MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT.
18 IN NO EVENT SHALL THE OPEN GROUP BE LIABLE FOR ANY CLAIM, DAMAGES OR
19 OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE,
20 ARISING FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR
21 OTHER DEALINGS IN THE SOFTWARE.
22
23 Except as contained in this notice, the name of The Open Group shall
24 not be used in advertising or otherwise to promote the sale, use or
25 other dealings in this Software without prior written authorization
26 from The Open Group.
27
28 */
29 /* $XFree86: xc/programs/xdm/greeter/verify.c,v 3.21 2001/12/14 20:01:29 dawes Exp $ */
30
31 /*
32 * xdm - display manager daemon
33 * Author: Keith Packard, MIT X Consortium
34 *
35 * verify.c
36 *
37 * typical unix verification routine.
38 */
39
40 #include <dm.h>
41
42 #include <pwd.h>
43 #ifdef USE_PAM
44 # include <security/pam_appl.h>
45 # include <stdlib.h>
46 #else
47 # ifdef HAVE_SHADOW_H
48 # include <shadow.h>
49 # include <errno.h>
50 # endif
51 #endif
52
53 # include <greet.h>
54
55 #include <wdmlib.h>
56
57 #ifdef QNX4
58 extern char *crypt(const char *, const char *);
59 #endif
60
61 static char *envvars[] = {
62 "TZ", /* SYSV and SVR4, but never hurts */
63 #if defined(sony) && !defined(SYSTYPE_SYSV) && !defined(_SYSTYPE_SYSV)
64 "bootdev",
65 "boothowto",
66 "cputype",
67 "ioptype",
68 "machine",
69 "model",
70 "CONSDEVTYPE",
71 "SYS_LANGUAGE",
72 "SYS_CODE",
73 #endif
74 #if (defined(SVR4) || defined(SYSV)) && defined(i386) && !defined(sun)
75 "XLOCAL",
76 #endif
77 NULL
78 };
79
80 #ifdef KERBEROS
81 #include <sys/param.h>
82 #include <kerberosIV/krb.h>
83 #include <kerberosIV/kafs.h>
84 static char krbtkfile[MAXPATHLEN];
85 #endif
86
87 static char **
userEnv(struct display * d,int useSystemPath,char * user,char * home,char * shell)88 userEnv (struct display *d, int useSystemPath, char *user, char *home, char *shell)
89 {
90 char **env;
91 char **envvar;
92 char *str;
93
94 env = defaultEnv ();
95 env = WDMSetEnv(env, "DISPLAY", d->name);
96 env = WDMSetEnv(env, "HOME", home);
97 env = WDMSetEnv(env, "LOGNAME", user); /* POSIX, System V */
98 env = WDMSetEnv(env, "USER", user); /* BSD */
99 env = WDMSetEnv(env, "PATH", useSystemPath ? d->systemPath : d->userPath);
100 env = WDMSetEnv(env, "SHELL", shell);
101 #ifdef KERBEROS
102 if (krbtkfile[0] != '\0')
103 env = WDMSetEnv(env, "KRBTKFILE", krbtkfile);
104 #endif
105 for (envvar = envvars; *envvar; envvar++)
106 {
107 str = getenv(*envvar);
108 if (str)
109 env = WDMSetEnv(env, *envvar, str);
110 }
111 return env;
112 }
113
114 #ifdef USE_PAM
115 static char *PAM_password;
116 static int pam_error;
117
PAM_conv(int num_msg,struct pam_message ** msg,struct pam_response ** resp,void * appdata_ptr)118 static int PAM_conv (int num_msg,
119 #ifdef sun
120 struct pam_message **msg,
121 #else
122 const struct pam_message **msg,
123 #endif
124 struct pam_response **resp,
125 void *appdata_ptr) {
126 int count = 0, replies = 0;
127 struct pam_response *reply = NULL;
128
129 #define PAM_RESPONSE_SIZE sizeof(struct pam_response)
130 size_t size = PAM_RESPONSE_SIZE;
131
132 #define COPY_STRING(s) (s) ? strdup(s) : (char*)NULL
133
134 for (count = 0; count < num_msg; count++) {
135 switch (msg[count]->msg_style) {
136 case PAM_PROMPT_ECHO_ON:
137 /* user name given to PAM already */
138 return PAM_CONV_ERR;
139 case PAM_PROMPT_ECHO_OFF:
140 /* wants password */
141 if (reply) {
142 reply = realloc(reply, size);
143 bzero(reply + size - PAM_RESPONSE_SIZE, PAM_RESPONSE_SIZE);
144 } else {
145 reply = (struct pam_response*)malloc(size);
146 bzero(reply, size);
147 }
148
149 if (!reply)
150 return PAM_CONV_ERR;
151
152 size += PAM_RESPONSE_SIZE;
153
154 reply[replies].resp_retcode = PAM_SUCCESS;
155 reply[replies].resp = COPY_STRING(PAM_password);
156 /* PAM frees resp */
157 break;
158 case PAM_TEXT_INFO:
159 /* ignore the informational mesage */
160 break;
161 default:
162 /* unknown or PAM_ERROR_MSG */
163 if (reply) free (reply);
164 return PAM_CONV_ERR;
165 }
166 }
167
168 #undef COPY_STRING
169 if (reply) *resp = reply;
170 return PAM_SUCCESS;
171 }
172
173 static struct pam_conv PAM_conversation = {
174 PAM_conv,
175 NULL
176 };
177 #endif /* USE_PAM */
178
179 #ifdef USE_BSDAUTH
180 int
Verify(struct display * d,struct greet_info * greet,struct verify_info * verify)181 Verify (struct display *d, struct greet_info *greet, struct verify_info *verify)
182 {
183 struct passwd *p;
184 login_cap_t *lc;
185 auth_session_t *as;
186 char *style, *shell, *home, *s, **argv;
187 char path[MAXPATHLEN];
188 int authok;
189
190 /* User may have specified an authentication style. */
191 if ((style = strchr(greet->name, ':')) != NULL)
192 *style++ = '\0';
193
194 Debug ("Verify %s, style %s ...\n", greet->name,
195 style ? style : "default");
196
197 p = getpwnam (greet->name);
198 endpwent();
199
200 if (!p || strlen (greet->name) == 0) {
201 Debug("getpwnam() failed.\n");
202 bzero(greet->password, strlen(greet->password));
203 return 0;
204 }
205
206 if ((lc = login_getclass(p->pw_class)) == NULL) {
207 Debug("login_getclass() failed.\n");
208 bzero(greet->password, strlen(greet->password));
209 return 0;
210 }
211 if ((style = login_getstyle(lc, style, "xdm")) == NULL) {
212 Debug("login_getstyle() failed.\n");
213 bzero(greet->password, strlen(greet->password));
214 return 0;
215 }
216 if ((as = auth_open()) == NULL) {
217 Debug("auth_open() failed.\n");
218 login_close(lc);
219 bzero(greet->password, strlen(greet->password));
220 return 0;
221 }
222 if (auth_setoption(as, "login", "yes") == -1) {
223 Debug("auth_setoption() failed.\n");
224 login_close(lc);
225 bzero(greet->password, strlen(greet->password));
226 return 0;
227 }
228
229 /* Set up state for no challenge, just check a response. */
230 auth_setstate(as, 0);
231 auth_setdata(as, "", 1);
232 auth_setdata(as, greet->password, strlen(greet->password) + 1);
233
234 /* Build path of the auth script and call it */
235 snprintf(path, sizeof(path), _PATH_AUTHPROG "%s", style);
236 auth_call(as, path, style, "-s", "response", greet->name, NULL);
237 authok = auth_getstate(as);
238
239 if ((authok & AUTH_ALLOW) == 0) {
240 Debug("password verify failed\n");
241 bzero(greet->password, strlen(greet->password));
242 auth_close(as);
243 login_close(lc);
244 return 0;
245 }
246 /* Run the approval script */
247 if (!auth_approval(as, lc, greet->name, "auth-xdm")) {
248 Debug("login not approved\n");
249 bzero(greet->password, strlen(greet->password));
250 auth_close(as);
251 login_close(lc);
252 return 0;
253 }
254 auth_close(as);
255 login_close(lc);
256 /* Check empty passwords against allowNullPasswd */
257 if (!greet->allow_null_passwd && strlen(greet->password) == 0) {
258 Debug("empty password not allowed\n");
259 return 0;
260 }
261 /* Only accept root logins if allowRootLogin resource is set */
262 if (p->pw_uid == 0 && !greet->allow_root_login) {
263 Debug("root logins not allowed\n");
264 bzero(greet->password, strlen(greet->password));
265 return 0;
266 }
267
268 /*
269 * Shell must be in /etc/shells
270 */
271 for (;;) {
272 s = getusershell();
273 if (s == NULL) {
274 /* did not found the shell in /etc/shells
275 -> failure */
276 Debug("shell not in /etc/shells\n");
277 bzero(greet->password, strlen(greet->password));
278 endusershell();
279 return 0;
280 }
281 if (strcmp(s, p->pw_shell) == 0) {
282 /* found the shell in /etc/shells */
283 endusershell();
284 break;
285 }
286 }
287 #else /* !USE_BSDAUTH */
288 int
289 Verify (struct display *d, struct greet_info *greet, struct verify_info *verify)
290 {
291 struct passwd *p;
292 #ifdef USE_PAM
293 pam_handle_t **pamhp = thepamhp();
294 #else
295 #ifdef HAVE_SHADOW_H
296 struct spwd *sp;
297 #endif
298 char *user_pass = NULL;
299 #endif
300 #ifdef __OpenBSD__
301 char *s;
302 struct timeval tp;
303 #endif
304 char *shell, *home;
305 char **argv;
306
307 WDMDebug("Verify %s ...\n", greet->name);
308 #ifndef USE_PAM
309 p = getpwnam (greet->name);
310 endpwent();
311
312 if (!p || strlen (greet->name) == 0) {
313 WDMDebug("getpwnam() failed.\n");
314 bzero(greet->password, strlen(greet->password));
315 return 0;
316 } else {
317 #ifdef linux
318 if (!strcmp(p->pw_passwd, "!") || !strcmp(p->pw_passwd, "*")) {
319 WDMDebug("The account is locked, no login allowed.\n");
320 bzero(greet->password, strlen(greet->password));
321 return 0;
322 }
323 #endif
324 user_pass = p->pw_passwd;
325 }
326 #endif
327 #ifdef KERBEROS
328 if(strcmp(greet->name, "root") != 0){
329 char name[ANAME_SZ];
330 char realm[REALM_SZ];
331 char *q;
332 int ret;
333
334 if(krb_get_lrealm(realm, 1)){
335 WDMDebug("Can't get Kerberos realm.\n");
336 } else {
337
338 sprintf(krbtkfile, "%s.%s", TKT_ROOT, d->name);
339 krb_set_tkt_string(krbtkfile);
340 unlink(krbtkfile);
341
342 ret = krb_verify_user(greet->name, "", realm,
343 greet->password, 1, "rcmd");
344
345 if(ret == KSUCCESS){
346 chown(krbtkfile, p->pw_uid, p->pw_gid);
347 WDMDebug("kerberos verify succeeded\n");
348 if (k_hasafs()) {
349 if (k_setpag() == -1)
350 WDMError("setpag() failed for %s\n",
351 greet->name);
352
353 if((ret = k_afsklog(NULL, NULL)) != KSUCCESS)
354 WDMError("Warning %s\n",
355 krb_get_err_text(ret));
356 }
357 goto done;
358 } else if(ret != KDC_PR_UNKNOWN && ret != SKDC_CANT){
359 /* failure */
360 WDMDebug("kerberos verify failure %d\n", ret);
361 krbtkfile[0] = '\0';
362 }
363 }
364 }
365 #endif
366 #ifndef USE_PAM
367 #ifdef HAVE_SHADOW_H
368 errno = 0;
369 sp = getspnam(greet->name);
370 if (sp == NULL) {
371 WDMDebug("getspnam() failed, errno=%d. Are you root?\n", errno);
372 } else {
373 user_pass = sp->sp_pwdp;
374 }
375 #ifndef QNX4
376 endspent();
377 #endif /* QNX4 doesn't need endspent() to end shadow passwd ops */
378 #endif
379 #if defined(ultrix) || defined(__ultrix__)
380 if (authenticate_user(p, greet->password, NULL) < 0)
381 #else
382 if (strcmp (crypt (greet->password, user_pass), user_pass))
383 #endif
384 {
385 if(!greet->allow_null_passwd || strlen(p->pw_passwd) > 0) {
386 WDMDebug("password verify failed\n");
387 bzero(greet->password, strlen(greet->password));
388 return 0;
389 } /* else: null passwd okay */
390 }
391 #ifdef KERBEROS
392 done:
393 #endif
394 #ifdef __OpenBSD__
395 /*
396 * Only accept root logins if allowRootLogin resource is set
397 */
398 if ((p->pw_uid == 0) && !greet->allow_root_login) {
399 WDMDebug("root logins not allowed\n");
400 bzero(greet->password, strlen(greet->password));
401 return 0;
402 }
403 /*
404 * Shell must be in /etc/shells
405 */
406 for (;;) {
407 s = getusershell();
408 if (s == NULL) {
409 /* did not found the shell in /etc/shells
410 -> failure */
411 WDMDebug("shell not in /etc/shells\n");
412 bzero(greet->password, strlen(greet->password));
413 endusershell();
414 return 0;
415 }
416 if (strcmp(s, p->pw_shell) == 0) {
417 /* found the shell in /etc/shells */
418 endusershell();
419 break;
420 }
421 }
422 /*
423 * Test for expired password
424 */
425 if (p->pw_change || p->pw_expire)
426 (void)gettimeofday(&tp, (struct timezone *)NULL);
427 if (p->pw_change) {
428 if (tp.tv_sec >= p->pw_change) {
429 WDMDebug("Password has expired.\n");
430 bzero(greet->password, strlen(greet->password));
431 return 0;
432 }
433 }
434 if (p->pw_expire) {
435 if (tp.tv_sec >= p->pw_expire) {
436 WDMDebug("account has expired.\n");
437 bzero(greet->password, strlen(greet->password));
438 return 0;
439 }
440 }
441 #endif /* __OpenBSD__ */
442 bzero(user_pass, strlen(user_pass)); /* in case shadow password */
443
444 #else /* USE_PAM */
445 #define PAM_BAIL \
446 if (pam_error != PAM_SUCCESS) goto pam_failed;
447
448 PAM_password = greet->password;
449 pam_error = pam_start("wdm", greet->name, &PAM_conversation, pamhp);
450 PAM_BAIL;
451 pam_error = pam_set_item(*pamhp, PAM_TTY, d->name);
452 PAM_BAIL;
453 pam_error = pam_set_item(*pamhp, PAM_RHOST, "");
454 PAM_BAIL;
455 pam_error = pam_authenticate(*pamhp, 0);
456 PAM_BAIL;
457 pam_error = pam_acct_mgmt(*pamhp, 0);
458 /* really should do password changing, but it doesn't fit well */
459 PAM_BAIL;
460 pam_error = pam_setcred(*pamhp, 0);
461 PAM_BAIL;
462 p = getpwnam (greet->name);
463 endpwent();
464
465 if (!p || strlen (greet->name) == 0) {
466 WDMDebug("getpwnam() failed.\n");
467 bzero(greet->password, strlen(greet->password));
468 return 0;
469 }
470
471 if (pam_error != PAM_SUCCESS) {
472 pam_failed:
473 pam_end(*pamhp, PAM_SUCCESS);
474 *pamhp = NULL;
475 return 0;
476 }
477 #undef PAM_BAIL
478 #endif /* USE_PAM */
479 #endif /* USE_BSDAUTH */
480
481 WDMDebug("verify succeeded\n");
482 /* The password is passed to StartClient() for use by user-based
483 authorization schemes. It is zeroed there. */
484 verify->uid = p->pw_uid;
485 verify->gid = p->pw_gid;
486 home = p->pw_dir;
487 shell = p->pw_shell;
488 argv = 0;
489 if (d->session)
490 argv = parseArgs (argv, d->session);
491 if (greet->string)
492 argv = parseArgs (argv, greet->string);
493 if (!argv)
494 argv = parseArgs (argv, "xsession");
495 verify->argv = argv;
496 verify->userEnviron = userEnv (d, p->pw_uid == 0,
497 greet->name, home, shell);
498 WDMDebug("user environment:\n");
499 WDMPrintEnv (verify->userEnviron);
500 verify->systemEnviron = systemEnv (d, greet->name, home);
501 WDMDebug("system environment:\n");
502 WDMPrintEnv (verify->systemEnviron);
503 WDMDebug("end of environments\n");
504 return 1;
505 }
506