xref: /freebsd/crypto/openssh/auth2.c (revision c68159a6d8eede11766cf13896d0f7670dbd51aa)
1 /*
2  * Copyright (c) 2000 Markus Friedl.  All rights reserved.
3  *
4  * Redistribution and use in source and binary forms, with or without
5  * modification, are permitted provided that the following conditions
6  * are met:
7  * 1. Redistributions of source code must retain the above copyright
8  *    notice, this list of conditions and the following disclaimer.
9  * 2. Redistributions in binary form must reproduce the above copyright
10  *    notice, this list of conditions and the following disclaimer in the
11  *    documentation and/or other materials provided with the distribution.
12  *
13  * THIS SOFTWARE IS PROVIDED BY THE AUTHOR ``AS IS'' AND ANY EXPRESS OR
14  * IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES
15  * OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED.
16  * IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR ANY DIRECT, INDIRECT,
17  * INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT
18  * NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE,
19  * DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY
20  * THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
21  * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF
22  * THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
23  */
24 
25 #include "includes.h"
26 RCSID("$OpenBSD: auth2.c,v 1.20 2000/10/14 12:16:56 markus Exp $");
27 RCSID("$FreeBSD$");
28 
29 #include <openssl/dsa.h>
30 #include <openssl/rsa.h>
31 #include <openssl/evp.h>
32 
33 #include "xmalloc.h"
34 #include "rsa.h"
35 #include "ssh.h"
36 #include "pty.h"
37 #include "packet.h"
38 #include "buffer.h"
39 #include "servconf.h"
40 #include "compat.h"
41 #include "channels.h"
42 #include "bufaux.h"
43 #include "ssh2.h"
44 #include "auth.h"
45 #include "session.h"
46 #include "dispatch.h"
47 #include "auth.h"
48 #include "key.h"
49 #include "kex.h"
50 
51 #include "dsa.h"
52 #include "uidswap.h"
53 #include "auth-options.h"
54 
55 #ifdef HAVE_LOGIN_CAP
56 #include <login_cap.h>
57 #endif /* HAVE_LOGIN_CAP */
58 
59 /* import */
60 extern ServerOptions options;
61 extern unsigned char *session_id2;
62 extern int session_id2_len;
63 
64 static Authctxt	*x_authctxt = NULL;
65 static int one = 1;
66 
67 typedef struct Authmethod Authmethod;
68 struct Authmethod {
69 	char	*name;
70 	int	(*userauth)(Authctxt *authctxt);
71 	int	*enabled;
72 };
73 
74 /* protocol */
75 
76 void	input_service_request(int type, int plen, void *ctxt);
77 void	input_userauth_request(int type, int plen, void *ctxt);
78 void	protocol_error(int type, int plen, void *ctxt);
79 
80 
81 /* helper */
82 Authmethod	*authmethod_lookup(const char *name);
83 struct passwd	*pwcopy(struct passwd *pw);
84 int	user_dsa_key_allowed(struct passwd *pw, Key *key);
85 char	*authmethods_get(void);
86 
87 /* auth */
88 int	userauth_none(Authctxt *authctxt);
89 int	userauth_passwd(Authctxt *authctxt);
90 int	userauth_pubkey(Authctxt *authctxt);
91 int	userauth_kbdint(Authctxt *authctxt);
92 
93 Authmethod authmethods[] = {
94 	{"none",
95 		userauth_none,
96 		&one},
97 	{"publickey",
98 		userauth_pubkey,
99 		&options.dsa_authentication},
100 	{"keyboard-interactive",
101 		userauth_kbdint,
102 		&options.kbd_interactive_authentication},
103 	{"password",
104 		userauth_passwd,
105 		&options.password_authentication},
106 	{NULL, NULL, NULL}
107 };
108 
109 /*
110  * loop until authctxt->success == TRUE
111  */
112 
113 void
114 do_authentication2()
115 {
116 	Authctxt *authctxt = xmalloc(sizeof(*authctxt));
117 	memset(authctxt, 'a', sizeof(*authctxt));
118 	authctxt->valid = 0;
119 	authctxt->attempt = 0;
120 	authctxt->success = 0;
121 	x_authctxt = authctxt;		/*XXX*/
122 
123 #ifdef KRB4
124 	/* turn off kerberos, not supported by SSH2 */
125 	options.krb4_authentication = 0;
126 #endif
127 	dispatch_init(&protocol_error);
128 	dispatch_set(SSH2_MSG_SERVICE_REQUEST, &input_service_request);
129 	dispatch_run(DISPATCH_BLOCK, &authctxt->success, authctxt);
130 	do_authenticated2();
131 }
132 
133 void
134 protocol_error(int type, int plen, void *ctxt)
135 {
136 	log("auth: protocol error: type %d plen %d", type, plen);
137 	packet_start(SSH2_MSG_UNIMPLEMENTED);
138 	packet_put_int(0);
139 	packet_send();
140 	packet_write_wait();
141 }
142 
143 void
144 input_service_request(int type, int plen, void *ctxt)
145 {
146 	Authctxt *authctxt = ctxt;
147 	unsigned int len;
148 	int accept = 0;
149 	char *service = packet_get_string(&len);
150 	packet_done();
151 
152 	if (authctxt == NULL)
153 		fatal("input_service_request: no authctxt");
154 
155 	if (strcmp(service, "ssh-userauth") == 0) {
156 		if (!authctxt->success) {
157 			accept = 1;
158 			/* now we can handle user-auth requests */
159 			dispatch_set(SSH2_MSG_USERAUTH_REQUEST, &input_userauth_request);
160 		}
161 	}
162 	/* XXX all other service requests are denied */
163 
164 	if (accept) {
165 		packet_start(SSH2_MSG_SERVICE_ACCEPT);
166 		packet_put_cstring(service);
167 		packet_send();
168 		packet_write_wait();
169 	} else {
170 		debug("bad service request %s", service);
171 		packet_disconnect("bad service request %s", service);
172 	}
173 	xfree(service);
174 }
175 
176 void
177 input_userauth_request(int type, int plen, void *ctxt)
178 {
179 	Authctxt *authctxt = ctxt;
180 	Authmethod *m = NULL;
181 	int authenticated = 0;
182 	char *user, *service, *method, *authmsg = NULL;
183 #ifdef HAVE_LOGIN_CAP
184 	login_cap_t *lc;
185 #endif /* HAVE_LOGIN_CAP */
186 #if defined(HAVE_LOGIN_CAP) || defined(LOGIN_ACCESS)
187 	const char *from_host, *from_ip;
188 
189 	from_host = get_canonical_hostname();
190 	from_ip = get_remote_ipaddr();
191 #endif /* HAVE_LOGIN_CAP || LOGIN_ACCESS */
192 
193 	if (authctxt == NULL)
194 		fatal("input_userauth_request: no authctxt");
195 	if (authctxt->attempt++ >= AUTH_FAIL_MAX)
196 		packet_disconnect("too many failed userauth_requests");
197 
198 	user = packet_get_string(NULL);
199 	service = packet_get_string(NULL);
200 	method = packet_get_string(NULL);
201 	debug("userauth-request for user %s service %s method %s", user, service, method);
202 	debug("attempt #%d", authctxt->attempt);
203 
204 	if (authctxt->attempt == 1) {
205 		/* setup auth context */
206 		struct passwd *pw = NULL;
207 		setproctitle("%s", user);
208 		pw = getpwnam(user);
209 		if (pw && allowed_user(pw) && strcmp(service, "ssh-connection")==0) {
210 			authctxt->pw = pwcopy(pw);
211 			authctxt->valid = 1;
212 			debug2("input_userauth_request: setting up authctxt for %s", user);
213 #ifdef USE_PAM
214 			start_pam(pw);
215 #endif
216 		} else {
217 			log("input_userauth_request: illegal user %s", user);
218 		}
219 		authctxt->user = xstrdup(user);
220 		authctxt->service = xstrdup(service);
221 	} else if (authctxt->valid) {
222 		if (strcmp(user, authctxt->user) != 0 ||
223 		    strcmp(service, authctxt->service) != 0) {
224 			log("input_userauth_request: missmatch: (%s,%s)!=(%s,%s)",
225 			    user, service, authctxt->user, authctxt->service);
226 			authctxt->valid = 0;
227 		}
228 	}
229 
230 #ifdef HAVE_LOGIN_CAP
231 	if (authctxt->pw != NULL) {
232 		lc = login_getpwclass(authctxt->pw);
233 		if (lc == NULL)
234 			lc = login_getclassbyname(NULL, authctxt->pw);
235 		if (!auth_hostok(lc, from_host, from_ip)) {
236 			log("Denied connection for %.200s from %.200s [%.200s].",
237 			    authctxt->pw->pw_name, from_host, from_ip);
238 			packet_disconnect("Sorry, you are not allowed to connect.");
239 		}
240 		if (!auth_timeok(lc, time(NULL))) {
241 			log("LOGIN %.200s REFUSED (TIME) FROM %.200s",
242 			    authctxt->pw->pw_name, from_host);
243 			packet_disconnect("Logins not available right now.");
244 		}
245 		login_close(lc);
246 		lc = NULL;
247 	}
248 #endif  /* HAVE_LOGIN_CAP */
249 #ifdef LOGIN_ACCESS
250 	if (authctxt->pw != NULL &&
251 	    !login_access(authctxt->pw->pw_name, from_host)) {
252 		log("Denied connection for %.200s from %.200s [%.200s].",
253 		    authctxt->pw->pw_name, from_host, from_ip);
254 		packet_disconnect("Sorry, you are not allowed to connect.");
255 	}
256 #endif /* LOGIN_ACCESS */
257 
258 	m = authmethod_lookup(method);
259 	if (m != NULL) {
260 		debug2("input_userauth_request: try method %s", method);
261 		authenticated =	m->userauth(authctxt);
262 	} else {
263 		debug2("input_userauth_request: unsupported method %s", method);
264 	}
265 	if (!authctxt->valid && authenticated == 1) {
266 		log("input_userauth_request: INTERNAL ERROR: authenticated invalid user %s service %s", user, method);
267 		authenticated = 0;
268 	}
269 
270 	/* Special handling for root */
271 	if (authenticated == 1 &&
272 	    authctxt->valid && authctxt->pw->pw_uid == 0 && !options.permit_root_login) {
273 		authenticated = 0;
274 		log("ROOT LOGIN REFUSED FROM %.200s", get_canonical_hostname());
275 	}
276 
277 #ifdef USE_PAM
278 	if (authenticated && authctxt->user && !do_pam_account(authctxt->user, NULL))
279 		authenticated = 0;
280 #endif /* USE_PAM */
281 
282 	/* Log before sending the reply */
283 	userauth_log(authctxt, authenticated, method);
284 	userauth_reply(authctxt, authenticated);
285 
286 	xfree(service);
287 	xfree(user);
288 	xfree(method);
289 }
290 
291 
292 void
293 userauth_log(Authctxt *authctxt, int authenticated, char *method)
294 {
295 	void (*authlog) (const char *fmt,...) = verbose;
296 	char *user = NULL, *authmsg = NULL;
297 
298 	/* Raise logging level */
299 	if (authenticated == 1 ||
300 	    !authctxt->valid ||
301 	    authctxt->attempt >= AUTH_FAIL_LOG ||
302 	    strcmp(method, "password") == 0)
303 		authlog = log;
304 
305 	if (authenticated == 1) {
306 		authmsg = "Accepted";
307 	} else if (authenticated == 0) {
308 		authmsg = "Failed";
309 	} else {
310 		authmsg = "Postponed";
311 	}
312 
313 	if (authctxt->valid) {
314 		user = authctxt->pw->pw_uid == 0 ? "ROOT" : authctxt->user;
315 	} else {
316 		user = "NOUSER";
317 	}
318 
319 	authlog("%s %s for %.200s from %.200s port %d ssh2",
320 	    authmsg,
321 	    method,
322 	    user,
323 	    get_remote_ipaddr(),
324 	    get_remote_port());
325 }
326 
327 void
328 userauth_reply(Authctxt *authctxt, int authenticated)
329 {
330 	/* XXX todo: check if multiple auth methods are needed */
331 	if (authenticated == 1) {
332 		/* turn off userauth */
333 		dispatch_set(SSH2_MSG_USERAUTH_REQUEST, &protocol_error);
334 		packet_start(SSH2_MSG_USERAUTH_SUCCESS);
335 		packet_send();
336 		packet_write_wait();
337 		/* now we can break out */
338 		authctxt->success = 1;
339 	} else if (authenticated == 0) {
340 		char *methods = authmethods_get();
341 		packet_start(SSH2_MSG_USERAUTH_FAILURE);
342 		packet_put_cstring(methods);
343 		packet_put_char(0);	/* XXX partial success, unused */
344 		packet_send();
345 		packet_write_wait();
346 		xfree(methods);
347 	} else {
348 		/* do nothing, we did already send a reply */
349 	}
350 }
351 
352 int
353 userauth_none(Authctxt *authctxt)
354 {
355 	/* disable method "none", only allowed one time */
356 	Authmethod *m = authmethod_lookup("none");
357 	if (m != NULL)
358 		m->enabled = NULL;
359 	packet_done();
360 #ifdef USE_PAM
361 	return authctxt->valid ? auth_pam_password(authctxt->pw, "") : 0;
362 #else /* !USE_PAM */
363 	return authctxt->valid ? auth_password(authctxt->pw, "") : 0;
364 #endif /* USE_PAM */
365 }
366 
367 int
368 userauth_passwd(Authctxt *authctxt)
369 {
370 	char *password;
371 	int authenticated = 0;
372 	int change;
373 	unsigned int len;
374 	change = packet_get_char();
375 	if (change)
376 		log("password change not supported");
377 	password = packet_get_string(&len);
378 	packet_done();
379 	if (authctxt->valid &&
380 #ifdef USE_PAM
381 	    auth_pam_password(authctxt->pw, password) == 1
382 #else
383 	    auth_password(authctxt->pw, password) == 1
384 #endif
385 	    )
386 		authenticated = 1;
387 	memset(password, 0, len);
388 	xfree(password);
389 	return authenticated;
390 }
391 
392 int
393 userauth_kbdint(Authctxt *authctxt)
394 {
395 	int authenticated = 0;
396 	char *lang = NULL;
397 	char *devs = NULL;
398 
399 	lang = packet_get_string(NULL);
400 	devs = packet_get_string(NULL);
401 	packet_done();
402 
403 	debug("keyboard-interactive language %s devs %s", lang, devs);
404 #ifdef SKEY
405 	/* XXX hardcoded, we should look at devs */
406 	if (options.skey_authentication != 0)
407 		authenticated = auth2_skey(authctxt);
408 #endif
409 	xfree(lang);
410 	xfree(devs);
411 	return authenticated;
412 }
413 
414 int
415 userauth_pubkey(Authctxt *authctxt)
416 {
417 	Buffer b;
418 	Key *key;
419 	char *pkalg, *pkblob, *sig;
420 	unsigned int alen, blen, slen;
421 	int have_sig;
422 	int authenticated = 0;
423 
424 	if (!authctxt->valid) {
425 		debug2("userauth_pubkey: disabled because of invalid user");
426 		return 0;
427 	}
428 	have_sig = packet_get_char();
429 	pkalg = packet_get_string(&alen);
430 	if (strcmp(pkalg, KEX_DSS) != 0) {
431 		log("bad pkalg %s", pkalg);	/*XXX*/
432 		xfree(pkalg);
433 		return 0;
434 	}
435 	pkblob = packet_get_string(&blen);
436 	key = dsa_key_from_blob(pkblob, blen);
437 	if (key != NULL) {
438 		if (have_sig) {
439 			sig = packet_get_string(&slen);
440 			packet_done();
441 			buffer_init(&b);
442 			if (datafellows & SSH_OLD_SESSIONID) {
443 				buffer_append(&b, session_id2, session_id2_len);
444 			} else {
445 				buffer_put_string(&b, session_id2, session_id2_len);
446 			}
447 			/* reconstruct packet */
448 			buffer_put_char(&b, SSH2_MSG_USERAUTH_REQUEST);
449 			buffer_put_cstring(&b, authctxt->user);
450 			buffer_put_cstring(&b,
451 			    datafellows & SSH_BUG_PUBKEYAUTH ?
452 			    "ssh-userauth" :
453 			    authctxt->service);
454 			buffer_put_cstring(&b, "publickey");
455 			buffer_put_char(&b, have_sig);
456 			buffer_put_cstring(&b, KEX_DSS);
457 			buffer_put_string(&b, pkblob, blen);
458 #ifdef DEBUG_DSS
459 			buffer_dump(&b);
460 #endif
461 			/* test for correct signature */
462 			if (user_dsa_key_allowed(authctxt->pw, key) &&
463 			    dsa_verify(key, sig, slen, buffer_ptr(&b), buffer_len(&b)) == 1)
464 				authenticated = 1;
465 			buffer_clear(&b);
466 			xfree(sig);
467 		} else {
468 			debug("test whether pkalg/pkblob are acceptable");
469 			packet_done();
470 
471 			/* XXX fake reply and always send PK_OK ? */
472 			/*
473 			 * XXX this allows testing whether a user is allowed
474 			 * to login: if you happen to have a valid pubkey this
475 			 * message is sent. the message is NEVER sent at all
476 			 * if a user is not allowed to login. is this an
477 			 * issue? -markus
478 			 */
479 			if (user_dsa_key_allowed(authctxt->pw, key)) {
480 				packet_start(SSH2_MSG_USERAUTH_PK_OK);
481 				packet_put_string(pkalg, alen);
482 				packet_put_string(pkblob, blen);
483 				packet_send();
484 				packet_write_wait();
485 				authenticated = -1;
486 			}
487 		}
488 		if (authenticated != 1)
489 			auth_clear_options();
490 		key_free(key);
491 	}
492 	xfree(pkalg);
493 	xfree(pkblob);
494 	return authenticated;
495 }
496 
497 /* get current user */
498 
499 struct passwd*
500 auth_get_user(void)
501 {
502 	return (x_authctxt != NULL && x_authctxt->valid) ? x_authctxt->pw : NULL;
503 }
504 
505 #define	DELIM	","
506 
507 char *
508 authmethods_get(void)
509 {
510 	Authmethod *method = NULL;
511 	unsigned int size = 0;
512 	char *list;
513 
514 	for (method = authmethods; method->name != NULL; method++) {
515 		if (strcmp(method->name, "none") == 0)
516 			continue;
517 		if (method->enabled != NULL && *(method->enabled) != 0) {
518 			if (size != 0)
519 				size += strlen(DELIM);
520 			size += strlen(method->name);
521 		}
522 	}
523 	size++;			/* trailing '\0' */
524 	list = xmalloc(size);
525 	list[0] = '\0';
526 
527 	for (method = authmethods; method->name != NULL; method++) {
528 		if (strcmp(method->name, "none") == 0)
529 			continue;
530 		if (method->enabled != NULL && *(method->enabled) != 0) {
531 			if (list[0] != '\0')
532 				strlcat(list, DELIM, size);
533 			strlcat(list, method->name, size);
534 		}
535 	}
536 	return list;
537 }
538 
539 Authmethod *
540 authmethod_lookup(const char *name)
541 {
542 	Authmethod *method = NULL;
543 	if (name != NULL)
544 		for (method = authmethods; method->name != NULL; method++)
545 			if (method->enabled != NULL &&
546 			    *(method->enabled) != 0 &&
547 			    strcmp(name, method->name) == 0)
548 				return method;
549 	debug2("Unrecognized authentication method name: %s", name ? name : "NULL");
550 	return NULL;
551 }
552 
553 /* return 1 if user allows given key */
554 int
555 user_dsa_key_allowed(struct passwd *pw, Key *key)
556 {
557 	char line[8192], file[1024];
558 	int found_key = 0;
559 	unsigned int bits = -1;
560 	FILE *f;
561 	unsigned long linenum = 0;
562 	struct stat st;
563 	Key *found;
564 
565 	if (pw == NULL)
566 		return 0;
567 
568 	/* Temporarily use the user's uid. */
569 	temporarily_use_uid(pw->pw_uid);
570 
571 	/* The authorized keys. */
572 	snprintf(file, sizeof file, "%.500s/%.100s", pw->pw_dir,
573 	    SSH_USER_PERMITTED_KEYS2);
574 
575 	/* Fail quietly if file does not exist */
576 	if (stat(file, &st) < 0) {
577 		/* Restore the privileged uid. */
578 		restore_uid();
579 		return 0;
580 	}
581 	/* Open the file containing the authorized keys. */
582 	f = fopen(file, "r");
583 	if (!f) {
584 		/* Restore the privileged uid. */
585 		restore_uid();
586 		return 0;
587 	}
588 	if (options.strict_modes) {
589 		int fail = 0;
590 		char buf[1024];
591 		/* Check open file in order to avoid open/stat races */
592 		if (fstat(fileno(f), &st) < 0 ||
593 		    (st.st_uid != 0 && st.st_uid != pw->pw_uid) ||
594 		    (st.st_mode & 022) != 0) {
595 			snprintf(buf, sizeof buf,
596 			    "%s authentication refused for %.100s: "
597 			    "bad ownership or modes for '%s'.",
598 			    key_type(key), pw->pw_name, file);
599 			fail = 1;
600 		} else {
601 			/* Check path to SSH_USER_PERMITTED_KEYS */
602 			int i;
603 			static const char *check[] = {
604 				"", SSH_USER_DIR, NULL
605 			};
606 			for (i = 0; check[i]; i++) {
607 				snprintf(line, sizeof line, "%.500s/%.100s",
608 				    pw->pw_dir, check[i]);
609 				if (stat(line, &st) < 0 ||
610 				    (st.st_uid != 0 && st.st_uid != pw->pw_uid) ||
611 				    (st.st_mode & 022) != 0) {
612 					snprintf(buf, sizeof buf,
613 					    "%s authentication refused for %.100s: "
614 					    "bad ownership or modes for '%s'.",
615 					    key_type(key), pw->pw_name, line);
616 					fail = 1;
617 					break;
618 				}
619 			}
620 		}
621 		if (fail) {
622 			fclose(f);
623 			log("%s",buf);
624 			restore_uid();
625 			return 0;
626 		}
627 	}
628 	found_key = 0;
629 	found = key_new(key->type);
630 
631 	while (fgets(line, sizeof(line), f)) {
632 		char *cp, *options = NULL;
633 		linenum++;
634 		/* Skip leading whitespace, empty and comment lines. */
635 		for (cp = line; *cp == ' ' || *cp == '\t'; cp++)
636 			;
637 		if (!*cp || *cp == '\n' || *cp == '#')
638 			continue;
639 
640 		bits = key_read(found, &cp);
641 		if (bits == 0) {
642 			/* no key?  check if there are options for this key */
643 			int quoted = 0;
644 			options = cp;
645 			for (; *cp && (quoted || (*cp != ' ' && *cp != '\t')); cp++) {
646 				if (*cp == '\\' && cp[1] == '"')
647 					cp++;	/* Skip both */
648 				else if (*cp == '"')
649 					quoted = !quoted;
650 			}
651 			/* Skip remaining whitespace. */
652 			for (; *cp == ' ' || *cp == '\t'; cp++)
653 				;
654 			bits = key_read(found, &cp);
655 			if (bits == 0) {
656 				/* still no key?  advance to next line*/
657 				continue;
658 			}
659 		}
660 		if (key_equal(found, key) &&
661 		    auth_parse_options(pw, options, linenum) == 1) {
662 			found_key = 1;
663 			debug("matching key found: file %s, line %ld",
664 			    file, linenum);
665 			break;
666 		}
667 	}
668 	restore_uid();
669 	fclose(f);
670 	key_free(found);
671 	return found_key;
672 }
673 
674 struct passwd *
675 pwcopy(struct passwd *pw)
676 {
677 	struct passwd *copy = xmalloc(sizeof(*copy));
678 	memset(copy, 0, sizeof(*copy));
679 	copy->pw_name = xstrdup(pw->pw_name);
680 	copy->pw_passwd = xstrdup(pw->pw_passwd);
681 	copy->pw_uid = pw->pw_uid;
682 	copy->pw_gid = pw->pw_gid;
683 	copy->pw_class = xstrdup(pw->pw_class);
684 	copy->pw_dir = xstrdup(pw->pw_dir);
685 	copy->pw_shell = xstrdup(pw->pw_shell);
686 	copy->pw_expire = pw->pw_expire;
687 	copy->pw_change = pw->pw_change;
688 	return copy;
689 }
690