1/*
2 * CDDL HEADER START
3 *
4 * The contents of this file are subject to the terms of the
5 * Common Development and Distribution License (the "License").
6 * You may not use this file except in compliance with the License.
7 *
8 * You can obtain a copy of the license at usr/src/OPENSOLARIS.LICENSE
9 * or http://www.opensolaris.org/os/licensing.
10 * See the License for the specific language governing permissions
11 * and limitations under the License.
12 *
13 * When distributing Covered Code, include this CDDL HEADER in each
14 * file and include the License file at usr/src/OPENSOLARIS.LICENSE.
15 * If applicable, add the following below this CDDL HEADER, with the
16 * fields enclosed by brackets "[]" replaced with your own identifying
17 * information: Portions Copyright [yyyy] [name of copyright owner]
18 *
19 * CDDL HEADER END
20 */
21/*
22 * Copyright 2009 Sun Microsystems, Inc.  All rights reserved.
23 * Use is subject to license terms.
24 */
25
26/*	Copyright (c) 1984, 1986, 1987, 1988, 1989 AT&T	*/
27/*	  All Rights Reserved  	*/
28
29/*
30 * University Copyright- Copyright (c) 1982, 1986, 1988
31 * The Regents of the University of California
32 * All Rights Reserved
33 *
34 * University Acknowledgment- Portions of this document are derived from
35 * software developed by the University of California, Berkeley, and its
36 * contributors.
37 */
38
39/*
40 * Administrative tool to add a new user to the publickey database
41 */
42#include <stdio.h>
43#include <stdlib.h>
44#include <rpc/rpc.h>
45#include <rpc/key_prot.h>
46#include <rpcsvc/ypclnt.h>
47#include <sys/wait.h>
48#include <netdb.h>
49#include <pwd.h>
50#include <shadow.h>
51#include <crypt.h>
52#include <string.h>
53#include <sys/resource.h>
54#include <netdir.h>
55#include <rpcsvc/nis.h>
56
57#define	MAXMAPNAMELEN	256
58#define	MAXPASSWD	256	/* max significant characters in password */
59
60#define	PK_FILES	1
61#define	PK_YP		2
62#define	PK_LDAP		4
63#define	DESCREDPASSLEN	sizeof (des_block)
64
65extern	int optind;
66extern	char *optarg;
67extern	int __getnetnamebyuid();
68extern  int self_check(char *name);
69
70#define	local_host(host_name)	self_check(host_name)
71
72char	*program_name;
73int		pk_database;
74static	char	*get_password();
75static	char *basename();
76static	char SHELL[] = "/bin/sh";
77static	char YPDBPATH[] = "/var/yp";
78static	char PKMAP[] = "publickey.byname";
79static	char UPDATEFILE[] = "updaters";
80static	char PKFILE[] = "/etc/publickey";
81static	void usage(void);
82
83int
84main(int argc, char *argv[])
85{
86	char	name[MAXNETNAMELEN + 1];
87	char	public[HEXKEYBYTES + 1];
88	char	secret[HEXKEYBYTES + 1];
89	char	crypt1[HEXKEYBYTES + KEYCHECKSUMSIZE + 1];
90	int	status;
91	char	*pass, *target_host = NULL,
92	    *username = NULL, *pk_service = NULL;
93	char	short_pass[DESCREDPASSLEN + 1];
94	struct passwd	*pw;
95	NCONF_HANDLE	*nc_handle;
96	struct	netconfig *nconf;
97	struct	nd_hostserv service;
98	struct	nd_addrlist *addrs;
99	bool_t	validhost;
100	uid_t	uid;
101	int	c;
102	char	host_pname[NIS_MAXNAMELEN];
103
104	program_name = argv[0];
105	while ((c = getopt(argc, argv, "s:u:h:")) != -1) {
106		switch (c) {
107		case 's':
108			if (pk_service == NULL)
109				pk_service = optarg;
110			else
111				usage();
112			break;
113		case 'u':
114			if (username || target_host)
115				usage();
116			username = optarg;
117			break;
118		case 'h':
119			if (username || target_host)
120				usage();
121			target_host = optarg;
122			break;
123		default:
124			usage();
125		}
126	}
127
128	if (optind < argc || (username == 0 && target_host == 0)) {
129		usage();
130	}
131
132	if ((pk_database = get_pk_source(pk_service)) == 0)
133		usage();
134
135	if (geteuid() != 0) {
136		(void) fprintf(stderr, "Must be superuser to run %s\n",
137		    program_name);
138		exit(1);
139	}
140
141	if (username) {
142		pw = getpwnam(username);
143		if (pw == NULL) {
144			(void) fprintf(stderr, "%s: unknown user: '%s'\n",
145			    program_name, username);
146			exit(1);
147		}
148		uid = pw->pw_uid;
149		if (uid == 0) {
150			if (! getnetname(name)) {
151				(void) fprintf(stderr,
152			"%s: could not get the equivalent netname for %s\n",
153				    program_name, username);
154				usage();
155			}
156			if (gethostname(host_pname, NIS_MAXNAMELEN)
157			    < 0) {
158				(void) fprintf(stderr,
159			"%s: could not get the hostname for %s\n",
160				    program_name, username);
161				usage();
162			}
163			target_host = host_pname;
164		}
165		if (__getnetnamebyuid(name, uid) == 0) {
166			(void) fprintf(stderr,
167			"%s: could not get the equivalent netname for %s\n",
168			    program_name, username);
169			usage();
170		}
171	} else {
172		/* -h hostname option */
173		service.h_host = target_host;
174		service.h_serv = NULL;
175		validhost = FALSE;
176		/* verify if this is a valid hostname */
177		nc_handle = setnetconfig();
178		if (nc_handle == NULL) {
179			/* fails to open netconfig file */
180			(void) fprintf(stderr,
181			"%s: failed in routine setnetconfig()\n",
182			    program_name);
183			exit(2);
184		}
185		while (nconf = getnetconfig(nc_handle)) {
186			/* check to see if hostname exists for this transport */
187			if ((netdir_getbyname(nconf, &service, &addrs) == 0) &&
188			    (addrs->n_cnt != 0)) {
189				/* at least one valid address */
190				validhost = TRUE;
191				break;
192			}
193		}
194		endnetconfig(nc_handle);
195		if (!validhost) {
196			(void) fprintf(stderr, "%s: unknown host: %s\n",
197			    program_name, target_host);
198			exit(1);
199		}
200		(void) host2netname(name, target_host, (char *)NULL);
201		uid = 0;
202	}
203
204	(void) fprintf(stdout, "Adding new key for %s.\n", name);
205	pass = get_password(uid, target_host, username);
206
207	if (pass == NULL)
208		exit(1);
209
210	(void) strlcpy(short_pass, pass, sizeof (short_pass));
211	(void) __gen_dhkeys(public, secret, short_pass);
212
213	(void) memcpy(crypt1, secret, HEXKEYBYTES);
214	(void) memcpy(crypt1 + HEXKEYBYTES, secret, KEYCHECKSUMSIZE);
215	crypt1[HEXKEYBYTES + KEYCHECKSUMSIZE] = 0;
216	xencrypt(crypt1, short_pass);
217
218	if (status = setpublicmap(name, public, crypt1, pk_database,
219	    short_pass)) {
220		switch (pk_database) {
221		case PK_YP:
222			(void) fprintf(stderr,
223			    "%s: unable to update NIS database (%u): %s\n",
224			    program_name, status,
225			    yperr_string(status));
226			break;
227		case PK_FILES:
228			(void) fprintf(stderr,
229			    "%s: hence, unable to update publickey database\n",
230			    program_name);
231			break;
232		default:
233			(void) fprintf(stderr,
234			    "%s: could not update unknown database: %d\n",
235			    program_name, pk_database);
236		}
237		exit(1);
238	}
239	return (0);
240}
241
242/*
243 * Set the entry in the public key file
244 */
245int
246setpublicmap(name, public, secret, database, pw)
247	int database;
248	char *name;
249	char *public;
250	char *secret;
251	char *pw;
252{
253	char pkent[HEXKEYBYTES + HEXKEYBYTES + KEYCHECKSUMSIZE + 2];
254	char *domain = NULL;
255	char *master = NULL;
256	char hostname[MAXHOSTNAMELEN+1];
257
258	(void) sprintf(pkent, "%s:%s", public, secret);
259	switch (database) {
260	case PK_YP:
261		/* check that we're on the master server */
262		(void) yp_get_default_domain(&domain);
263		if (yp_master(domain, PKMAP, &master) != 0) {
264			(void) fprintf(stderr,
265			"%s: cannot find master of NIS publickey database\n",
266				program_name);
267			exit(1);
268		}
269		if (gethostname(hostname, MAXHOSTNAMELEN) < 0) {
270			(void) fprintf(stderr,
271				"%s: cannot find my own host name\n",
272				program_name);
273			exit(1);
274		}
275		if (strcmp(master, hostname) != 0) {
276			(void) fprintf(stderr,
277			"%s: can only be used on NIS master machine '%s'\n",
278				program_name, master);
279			exit(1);
280		}
281
282		if (chdir(YPDBPATH) < 0) {
283			(void) fprintf(stderr, "%s: cannot chdir to %s",
284			program_name, YPDBPATH);
285		}
286		(void) fprintf(stdout,
287			"Please wait for the database to get updated ...\n");
288		return (mapupdate(name, PKMAP, YPOP_STORE, pkent));
289	case PK_FILES:
290		return (localupdate(name, PKFILE, YPOP_STORE, pkent));
291	case PK_LDAP:
292		return (ldap_update("dh192-0", name, public, secret, pw));
293	default:
294		break;
295	}
296	return (1);
297}
298
299void
300usage(void)
301{
302	(void) fprintf(stderr,
303	    "usage:\t%s -u username [-s ldap | nis | files]\n",
304	    program_name);
305	(void) fprintf(stderr,
306	    "\t%s -h hostname [-s ldap | nis | files]\n",
307	    program_name);
308	exit(1);
309}
310
311/*
312 * The parameters passed into the routine get_password and the
313 * return values are as follows:
314 * If the -h flag was specified on the command line:
315 * (a) username is null
316 * (b) target_host is non-null
317 * (c) uid is 0
318 * (d) the login password of root on target_host is returned
319 *
320 * If the -u flag was specified on the command line:
321 * (a) username is non-null
322 * (b) target_host is null in all cases except when username is root;
323 *	in that case target_host is set to the local host
324 * (c) uid is set to the username's uid
325 * (d) the login password of the user <username> is returned
326 */
327static char *
328get_password(uid, target_host, username)
329uid_t	uid;
330char	*target_host;
331char	*username;
332{
333	static	char	password[MAXPASSWD+1];
334	char		prompt[MAXPASSWD+MAXHOSTNAMELEN+64];
335	char		*encrypted_password,
336			*login_password = NULL,
337			*pass = NULL;
338	struct	passwd	*pw;
339	struct	spwd	*spw;
340
341	if ((username != 0) ||
342	    (target_host != 0) && (local_host(target_host))) {
343
344	/*
345	 * "-u username" or "-h localhost" was specified on the
346	 * command line
347	 */
348
349	pw = getpwuid(uid);
350
351	if (! pw) {
352		(void) fprintf(stderr,
353			"%s: unable to locate password record for uid %d\n",
354			program_name, uid);
355		return (0);
356	}
357	spw = getspnam(pw->pw_name);
358	if (spw)
359		login_password = spw->sp_pwdp;
360
361	if (! login_password || (strlen(login_password) == 0)) {
362		(void) fprintf(stderr,
363			"%s: unable to locate shadow password record for %s\n",
364			program_name, pw->pw_name);
365		return (0);
366	}
367
368	if (uid == 0) {
369		(void) sprintf(prompt, "Enter local root login password:");
370	} else
371	    (void) sprintf(prompt, "Enter %s's login password:",
372		pw->pw_name);
373
374	pass = getpassphrase(prompt);
375	if (pass && strlen(pass) == 0) {
376		(void) fprintf(stderr, "%s: Invalid password.\n",
377			program_name);
378		return (0);
379	}
380	strcpy(password, pass);
381	encrypted_password = crypt(password, login_password);
382
383	/* Verify that password supplied matches login password */
384	if (strcmp(encrypted_password, login_password) != 0) {
385		/*
386		 * Give another chance for typo
387		 */
388		pass = getpassphrase("Please retype password:");
389	    if (pass && strlen(pass) == 0) {
390		(void) fprintf(stderr, "%s: Invalid password.\n",
391			program_name);
392		return (0);
393	    }
394	    strcpy(password, pass);
395	    encrypted_password = crypt(password, login_password);
396	    if (strcmp(encrypted_password, login_password) != 0) {
397		    (void) fprintf(stderr,
398			"%s: ERROR, invalid password.\n",
399			program_name);
400		    return (0);
401	    }
402	}
403	} else {
404		/*
405		 * "-h remotehost" was specified on the command line
406		 *
407		 * Since we cannot verify the root password of the remote
408		 * host we have to trust what the user inputs. We can,
409		 * however, reduce the possibility of an  error by prompting
410		 * the user to enter the target host's password twice and
411		 * comparing those two. We can also authenticate the
412		 * user to be root by checking the real uid.
413		 */
414
415		if (getuid() != 0) {
416			(void) fprintf(stderr, "Must be superuser to run %s\n",
417			    program_name);
418			return (0);
419		}
420
421		(void) sprintf(prompt,
422		    "Enter %s's root login password:",
423		    target_host);
424		pass = getpassphrase(prompt);
425		if (!pass) {
426			(void) fprintf(stderr,
427			    "%s: getpass failed.\n",
428			    program_name);
429			return (0);
430		}
431		if (!*pass) {
432			(void) fprintf(stderr,
433			    "%s: Invalid root password.\n",
434			    program_name);
435			return (0);
436		}
437		strcpy(password, pass);
438
439		/*
440		 * Now re-enter the password and compare it to the
441		 * one just read.
442		 */
443		(void) sprintf(prompt,
444		    "Please confirm %s's root login password:",
445		    target_host);
446		pass = getpassphrase(prompt);
447		if (!pass) {
448			(void) fprintf(stderr,
449			    "%s: getpass failed.\n",
450			    program_name);
451			return (0);
452		}
453		if (!*pass) {
454			(void) fprintf(stderr,
455			    "%s: Invalid root password.\n",
456			    program_name);
457			return (0);
458		}
459		if (strcmp(pass, password) != 0) {
460			(void) fprintf(stderr,
461			    "%s: Password Incorrect.\n",
462			    program_name);
463			return (0);
464		}
465	}
466
467	return (password);
468}
469