xref: /illumos-gate/usr/src/cmd/keyserv/chkey_common.c (revision f498645a3eecf2ddd304b4ea9c7f1b4c155ff79e)
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, Version 1.0 only
6  * (the "License").  You may not use this file except in compliance
7  * with the License.
8  *
9  * You can obtain a copy of the license at usr/src/OPENSOLARIS.LICENSE
10  * or http://www.opensolaris.org/os/licensing.
11  * See the License for the specific language governing permissions
12  * and limitations under the License.
13  *
14  * When distributing Covered Code, include this CDDL HEADER in each
15  * file and include the License file at usr/src/OPENSOLARIS.LICENSE.
16  * If applicable, add the following below this CDDL HEADER, with the
17  * fields enclosed by brackets "[]" replaced with your own identifying
18  * information: Portions Copyright [yyyy] [name of copyright owner]
19  *
20  * CDDL HEADER END
21  */
22 /*
23  * Copyright 2005 Sun Microsystems, Inc.  All rights reserved.
24  * Use is subject to license terms.
25  */
26 
27 #pragma ident	"%Z%%M%	%I%	%E% SMI"
28 
29 #include <stdio.h>
30 #include <stdlib.h>
31 #include <ctype.h>
32 #include <strings.h>
33 #include <pwd.h>
34 #include <shadow.h>
35 #include <netdb.h>
36 #include <mp.h>
37 #include <rpcsvc/nis.h>
38 #include <rpc/key_prot.h>
39 #include <nsswitch.h>
40 #include <ns_sldap.h>
41 
42 extern char *crypt();
43 extern long random();
44 extern char *getpassphrase();
45 extern char *program_name;
46 static const char *CRED_TABLE = "cred.org_dir";
47 
48 #define	ROOTKEY_FILE	"/etc/.rootkey"
49 
50 #ifndef MAXHOSTNAMELEN
51 #define	MAXHOSTNAMELEN 256
52 #endif
53 
54 #define	PK_FILES	1
55 #define	PK_YP		2
56 #define	PK_NISPLUS	3
57 #define	PK_LDAP		4
58 
59 #define	LDAP_BINDDN_DEFAULT	"cn=Directory Manager"
60 #define	PROMPTGET_SUCCESS	1
61 #define	PROMPTGET_FAIL		-1
62 #define	PROMPTGET_MEMORY_FAIL	-2
63 #define	PASSWD_UNMATCHED	-3
64 
65 #define	FREE_CREDINFO(s) \
66 	if ((s)) { (void) memset((s), 0, strlen((s))); }
67 
68 
69 /* ************************ switch functions *************************** */
70 
71 /*	NSW_NOTSUCCESS  NSW_NOTFOUND   NSW_UNAVAIL    NSW_TRYAGAIN */
72 #define	DEF_ACTION {__NSW_RETURN, __NSW_RETURN, __NSW_CONTINUE, __NSW_CONTINUE}
73 
74 static struct __nsw_lookup lookup_files = {"files", DEF_ACTION, NULL, NULL},
75 		lookup_nis = {"nis", DEF_ACTION, NULL, &lookup_files};
76 static struct __nsw_switchconfig publickey_default =
77 			{0, "publickey", 2, &lookup_nis};
78 
79 static int get_ldap_bindDN(char **);
80 static int get_ldap_bindPassword(char **);
81 
82 /*
83  * Prompt the users for a ldap bind DN. If users do not enter a value but just
84  * simply hit the return key, the default bindDN "cn=Directory Manager"
85  * will be used.
86  */
87 static int
88 get_ldap_bindDN(char **ret_bindDN) {
89 
90 	char	bindDN[BUFSIZ];
91 	char	prompt[BUFSIZ];
92 	int	blen, pos;
93 
94 	/* set the initial value for bindDN buffer */
95 	(void) memset(bindDN, 0, BUFSIZ);
96 
97 	(void) snprintf(prompt, BUFSIZ,
98 	"\nThe LDAP bind DN and password are required for this update.\n"
99 	"If you are not sure what values to enter, please contact your\n"
100 	"LDAP administrator.\n\nPlease enter LDAP bind DN [%s]: ",
101 	LDAP_BINDDN_DEFAULT);
102 
103 	printf(prompt);
104 
105 	if (fgets(bindDN, sizeof (bindDN), stdin) == NULL) {
106 		(void) strlcpy(bindDN, LDAP_BINDDN_DEFAULT, BUFSIZ);
107 	}
108 
109 	blen = strlen(bindDN);
110 
111 	/* Check if the buffer ends with a newline */
112 	if ((blen > 0) && (bindDN[blen - 1] == '\n')) {
113 		bindDN[blen - 1] = '\0';
114 		blen -= 1;
115 	}
116 
117 	/* Remove the white spaces */
118 	if (blen > 0) {
119 		for (pos = blen - 1; pos >= 0; pos--) {
120 			if (isspace(bindDN[pos]))
121 				bindDN[pos] = '\0';
122 			else
123 				break;
124 		}
125 	}
126 
127 	/* Use the default bindDN, if the buffer contains no characters */
128 	if (strlen(bindDN) == 0)
129 		(void) strlcpy(bindDN, LDAP_BINDDN_DEFAULT, BUFSIZ);
130 
131 	if ((*ret_bindDN = (char *)malloc(strlen(bindDN)+1)) == NULL) {
132 		(void) memset(bindDN, 0, BUFSIZ);
133 		return (PROMPTGET_MEMORY_FAIL);
134 	}
135 
136 	(void) strlcpy(*ret_bindDN, bindDN, strlen(bindDN)+1);
137 
138 	/* Clean up and erase the credential info */
139 	(void) memset(bindDN, 0, BUFSIZ);
140 
141 	return (PROMPTGET_SUCCESS);
142 }
143 
144 
145 /*
146  * Prompt the user for a ldap bind password.
147  */
148 static int
149 get_ldap_bindPassword(char **ret_bindPass) {
150 
151 	char 	bindPassword[BUFSIZ];
152 	char	prompt[BUFSIZ];
153 	char	*bindPass = NULL;
154 
155 	/* set the initial value for bindPassword buffer */
156 	(void) memset(bindPassword, 0, BUFSIZ);
157 	*ret_bindPass = NULL;
158 
159 	(void) snprintf(prompt, BUFSIZ,
160 		"Please enter LDAP bind password: ");
161 
162 	bindPass = getpassphrase(prompt);
163 
164 	if (bindPass == NULL)
165 		return (PROMPTGET_FAIL);
166 
167 	(void) strlcpy(bindPassword, bindPass, BUFSIZ);
168 
169 	/* clean the static buffer returned from getpassphrase call */
170 	(void) memset(bindPass, 0, strlen(bindPass));
171 	bindPass = NULL;
172 
173 	/*
174 	 * Re-enter the bind passowrd and compare it with the one
175 	 * from previous entered.
176 	 */
177 	(void) snprintf(prompt, BUFSIZ,
178 		"Re-enter LDAP bind password to confirm: ");
179 
180 	bindPass = getpassphrase(prompt);
181 
182 	if (bindPass == NULL) {
183 		(void) memset(bindPassword, 0, BUFSIZ);
184 		return (PASSWD_UNMATCHED);
185 	}
186 
187 	if (strcmp(bindPass, bindPassword) != 0) {
188 		(void) memset(bindPassword, 0, BUFSIZ);
189 		(void) memset(bindPass, 0, strlen(bindPass));
190 		return (PASSWD_UNMATCHED);
191 	} else {
192 		(void) memset(bindPass, 0, strlen(bindPass));
193 		if ((*ret_bindPass = (char *)malloc(strlen(bindPassword)+1))
194 			== NULL) {
195 			(void) memset(bindPassword, 0, BUFSIZ);
196 			return (PROMPTGET_MEMORY_FAIL);
197 		}
198 
199 		(void) strlcpy(*ret_bindPass, bindPassword,
200 			strlen(bindPassword)+1);
201 
202 		/* Clean up and erase the credential info */
203 		(void) memset(bindPassword, 0, BUFSIZ);
204 
205 		return (PROMPTGET_SUCCESS);
206 	}
207 }
208 
209 
210 
211 char *
212 switch_policy_str(struct __nsw_switchconfig *conf)
213 {
214 	struct __nsw_lookup *look;
215 	static char policy[256];  /* 256 is enough for (nis, files...etc) */
216 	int previous = 0;
217 
218 	memset((char *)policy, 0, 256);
219 
220 	for (look = conf->lookups; look; look = look->next) {
221 		if (previous)
222 			strcat(policy, " ");
223 		strcat(policy, look->service_name);
224 		previous = 1;
225 	}
226 	return (policy);
227 }
228 
229 int
230 no_switch_policy(struct __nsw_switchconfig *conf)
231 {
232 	return (conf == NULL || conf->lookups == NULL);
233 }
234 
235 int
236 is_switch_policy(struct __nsw_switchconfig *conf, char *target)
237 {
238 	return (conf &&
239 		conf->lookups &&
240 		strcmp(conf->lookups->service_name, target) == 0 &&
241 		conf->lookups->next == NULL);
242 }
243 
244 char *
245 first_and_only_switch_policy(char *policy,
246 		    struct __nsw_switchconfig *default_conf,
247 		    char *head_msg)
248 {
249 	struct __nsw_switchconfig *conf;
250 	enum __nsw_parse_err perr;
251 	int policy_correct = 1;
252 	char *target_service = 0;
253 	int use_default = 0;
254 
255 	if (default_conf == 0)
256 		default_conf = &publickey_default;
257 
258 	conf = __nsw_getconfig(policy, &perr);
259 	if (no_switch_policy(conf)) {
260 		use_default = 1;
261 		conf = default_conf;
262 	}
263 
264 	target_service = conf->lookups->service_name;
265 
266 	if (conf->lookups->next != NULL) {
267 		policy_correct = 0;
268 		if (use_default) {
269 			(void) fprintf(stderr,
270 			"\n%s\n There is no publickey entry in %s.\n",
271 				head_msg, __NSW_CONFIG_FILE);
272 			(void) fprintf(stderr,
273 			"The default publickey policy is \"publickey: %s\".\n",
274 			switch_policy_str(default_conf));
275 		} else
276 			(void) fprintf(stderr,
277 		"\n%s\nThe publickey entry in %s is \"publickey: %s\".\n",
278 			head_msg, __NSW_CONFIG_FILE,
279 			switch_policy_str(conf));
280 	}
281 
282 	if (policy_correct == 0)
283 		(void) fprintf(stderr,
284 	"I cannot figure out which publickey database you want to update.\n");
285 	if (!use_default && conf)
286 		__nsw_freeconfig(conf);
287 
288 	if (policy_correct)
289 		return (target_service);
290 	else
291 		return (0);
292 }
293 
294 
295 
296 int
297 check_switch_policy(char *policy, char *target_service,
298 		    struct __nsw_switchconfig *default_conf,
299 		    char *head_msg, char *tail_msg)
300 {
301 	struct __nsw_switchconfig *conf;
302 	enum __nsw_parse_err perr;
303 	int policy_correct = 1;
304 
305 	if (default_conf == 0)
306 		default_conf = &publickey_default;
307 
308 	conf = __nsw_getconfig(policy, &perr);
309 	if (no_switch_policy(conf)) {
310 		if (!is_switch_policy(default_conf, target_service)) {
311 			(void) fprintf(stderr,
312 				"\n%s\nThere is no publickey entry in %s.\n",
313 				head_msg, __NSW_CONFIG_FILE);
314 			(void) fprintf(stderr,
315 			"The default publickey policy is \"publickey: %s\".\n",
316 			switch_policy_str(default_conf));
317 			policy_correct = 0;
318 		}
319 	} else if (!is_switch_policy(conf, target_service)) {
320 		(void) fprintf(stderr,
321 		"\n%s\nThe publickey entry in %s is \"publickey: %s\".\n",
322 			head_msg, __NSW_CONFIG_FILE,
323 			switch_policy_str(conf));
324 		policy_correct = 0;
325 	}
326 	/* should we exit ? */
327 	if (policy_correct == 0)
328 		(void) fprintf(stderr,
329 		"It should be \"publickey: %s\"%s\n\n",
330 			target_service, tail_msg);
331 	if (conf)
332 		__nsw_freeconfig(conf);
333 
334 	return (policy_correct);
335 }
336 
337 int
338 get_pk_source(char *pk_service)
339 {
340 	int db = 0, got_from_switch = 0;
341 
342 	/* No service specified, try to figure out from switch */
343 	if (pk_service == 0) {
344 		pk_service = first_and_only_switch_policy("publickey", 0,
345 				"ERROR:");
346 		if (pk_service == 0)
347 			return (0);
348 		(void) fprintf(stdout,
349 			"Updating %s publickey database.\n",
350 			pk_service);
351 		got_from_switch = 1;
352 	}
353 
354 	if (strcmp(pk_service, "ldap") == 0)
355 		db = PK_LDAP;
356 	else if (strcmp(pk_service, "nisplus") == 0)
357 		db = PK_NISPLUS;
358 	else if (strcmp(pk_service, "nis") == 0)
359 		db = PK_YP;
360 	else if (strcmp(pk_service, "files") == 0)
361 		db = PK_FILES;
362 	else return (0);
363 
364 	/*
365 	 * If we didn't get service name from switch, check switch
366 	 * and print warning about it source of publickeys if not unique
367 	 */
368 	if (got_from_switch == 0)
369 		check_switch_policy("publickey", pk_service, 0, "WARNING:",
370 				    db == PK_FILES ? "" :
371 			    "; add 'files' if you want the 'nobody' key.");
372 
373 
374 	return (db); /* all passed */
375 }
376 
377 
378 /* ***************************** keylogin stuff *************************** */
379 int
380 keylogin(char *netname, char *secret)
381 {
382 	struct key_netstarg netst;
383 
384 	netst.st_pub_key[0] = 0;
385 	memcpy(netst.st_priv_key, secret, HEXKEYBYTES);
386 	netst.st_netname = netname;
387 
388 #ifdef NFS_AUTH
389 	nra.authtype = AUTH_DES;	/* only revoke DES creds */
390 	nra.uid = getuid();		/* use the real uid */
391 	if (_nfssys(NFS_REVAUTH, &nra) < 0) {
392 		perror("Warning: NFS credentials not destroyed");
393 		err = 1;
394 	}
395 #endif
396 
397 
398 	/* do actual key login */
399 	if (key_setnet(&netst) < 0) {
400 		(void) fprintf(stderr,
401 			"Could not set %s's secret key\n", netname);
402 		(void) fprintf(stderr, "May be the keyserv is down?\n");
403 		return (0);
404 	}
405 
406 	return (1);
407 }
408 
409 /*
410  * XXX unused routine.
411  * Can not locate any routine that is using this write_rootkey()
412  * function. There are 2 other write_rootkey() routines defined in chkey.c
413  * and in cmd/rpcsvc/nis/utils/nisaddcred/makedhextcred.c.
414  *
415  * write unencrypted secret key into root key file
416  */
417 void
418 write_rootkey(char *secret)
419 {
420 	char sbuf[HEXKEYBYTES+2];
421 	int fd, len;
422 
423 	strcpy(sbuf, secret);
424 	strcat(sbuf, "\n");
425 	len = strlen(sbuf);
426 	sbuf[len] = '\0';
427 	unlink(ROOTKEY_FILE);
428 	if ((fd = open(ROOTKEY_FILE, O_WRONLY+O_CREAT, 0600)) != -1) {
429 		write(fd, sbuf, len+1);
430 		close(fd);
431 		(void) fprintf(stdout, "Wrote secret key into %s\n",
432 			ROOTKEY_FILE);
433 	} else {
434 		(void) fprintf(stderr, "Could not open %s for update\n",
435 			ROOTKEY_FILE);
436 		perror(ROOTKEY_FILE);
437 	}
438 }
439 
440 /* ****************************** nisplus stuff ************************* */
441 /* most of it gotten from nisaddcred */
442 
443 
444 /* Check that someone else don't have the same auth information already */
445 static
446 nis_error
447 auth_exists(char *princname, char *auth_name, char *auth_type, char *domain)
448 {
449 	char sname[NIS_MAXNAMELEN+MAXHOSTNAMELEN+64];
450 	nis_result	*res;
451 	nis_error status;
452 	char *foundprinc;
453 
454 	(void) sprintf(sname, "[auth_name=%s,auth_type=%s],%s.%s",
455 		auth_name, auth_type, CRED_TABLE, domain);
456 	if (sname[strlen(sname)-1] != '.')
457 		strcat(sname, ".");
458 	/* Don't want FOLLOW_PATH here */
459 	res = nis_list(sname,
460 		MASTER_ONLY+USE_DGRAM+NO_AUTHINFO+FOLLOW_LINKS,
461 		NULL, NULL);
462 
463 	status = res->status;
464 	switch (res->status) {
465 	case NIS_NOTFOUND:
466 		break;
467 	case NIS_TRYAGAIN :
468 		(void) fprintf(stderr,
469 			"%s: NIS+ server busy, try again later.\n",
470 			program_name);
471 		exit(1);
472 	case NIS_PERMISSION :
473 		(void) fprintf(stderr,
474 		"%s: insufficient permission to look up old credentials.\n",
475 			program_name);
476 		exit(1);
477 	case NIS_SUCCESS:
478 		foundprinc = ENTRY_VAL(res->objects.objects_val, 0);
479 		if (nis_dir_cmp(foundprinc, princname) != SAME_NAME) {
480 			(void) fprintf(stderr,
481 	"%s: %s credentials with auth_name '%s' already belong to '%s'.\n",
482 			program_name, auth_type, auth_name, foundprinc);
483 			exit(1);
484 		}
485 		break;
486 	default:
487 		(void) fprintf(stderr,
488 			"%s: error looking at cred table, NIS+ error: %s\n",
489 			program_name, nis_sperrno(res->status));
490 		exit(1);
491 	}
492 	nis_freeresult(res);
493 	return (status);
494 }
495 
496 /* Returns 0 if check fails; 1 if successful. */
497 static int
498 sanity_checks(nis_princ, netname, domain)
499 char	*nis_princ,
500 	*netname,
501 	*domain;
502 {
503 	char	netdomainaux[MAXHOSTNAMELEN+1];
504 	char	*princdomain, *netdomain;
505 	int	len;
506 
507 	/* Sanity check 0. Do we have a nis+ principal name to work with? */
508 	if (nis_princ == NULL) {
509 		(void) fprintf(stderr,
510 		"%s: you must create a \"LOCAL\" credential for '%s' first.\n",
511 			program_name, netname);
512 		(void) fprintf(stderr, "\tSee nisaddcred(1).\n");
513 		return (0);
514 	}
515 
516 	/* Sanity check 0.5.  NIS+ principal names must be dotted. */
517 	len = strlen(nis_princ);
518 	if (nis_princ[len-1] != '.') {
519 		(void) fprintf(stderr,
520 		"%s: invalid principal name: '%s' (forgot ending dot?).\n",
521 			program_name, nis_princ);
522 		return (0);
523 	}
524 
525 	/* Sanity check 1.  We only deal with one type of netnames. */
526 	if (strncmp(netname, "unix", 4) != 0) {
527 		(void) fprintf(stderr,
528 			"%s: unrecognized netname type: '%s'.\n",
529 			program_name, netname);
530 		return (0);
531 	}
532 
533 	/* Sanity check 2.  Should only add DES cred in home domain. */
534 	princdomain = nis_domain_of(nis_princ);
535 	if (strcasecmp(princdomain, domain) != 0) {
536 		(void) fprintf(stderr,
537 "%s: domain of principal '%s' does not match destination domain '%s'.\n",
538 			program_name, nis_princ, domain);
539 		(void) fprintf(stderr,
540 	"Should only add DES credential of principal in its home domain\n");
541 		return (0);
542 	}
543 
544 	/*
545 	 * Sanity check 3:  Make sure netname's domain same as principal's
546 	 * and don't have extraneous dot at the end.
547 	 */
548 	netdomain = (char *)strchr(netname, '@');
549 	if (! netdomain || netname[strlen(netname)-1] == '.') {
550 		(void) fprintf(stderr, "%s: invalid netname: '%s'. \n",
551 			program_name, netname);
552 		return (0);
553 	}
554 	netdomain++; /* skip '@' */
555 	/* make sure we don't run into buffer overflow */
556 	if (strlen(netdomain) > sizeof (netdomainaux))
557 		return (0);
558 	strcpy(netdomainaux, netdomain);
559 	if (netdomainaux[strlen(netdomainaux)-1] != '.')
560 		strcat(netdomainaux, ".");
561 
562 	if (strcasecmp(princdomain, netdomainaux) != 0) {
563 		(void) fprintf(stderr,
564 	"%s: domain of netname %s should be same as that of principal %s\n",
565 			program_name, netname, nis_princ);
566 		return (0);
567 	}
568 
569 	/* Another principal owns same credentials? (exits if that happens) */
570 	(void) auth_exists(nis_princ, netname, "DES", domain);
571 
572 	return (1); /* all passed */
573 }
574 
575 
576 /*
577  * Similar to nis_local_principal in "nis_subr.c" except
578  * this gets the results from the MASTER_ONLY and no FOLLOW_PATH.
579  * We only want the master because we'll be making updates there,
580  * and also the replicas may not have seen the 'nisaddacred local'
581  * that may have just occurred.
582  * Returns NULL if not found.
583  */
584 char *
585 get_nisplus_principal(directory, uid)
586 char	*directory;
587 uid_t	uid;
588 {
589 	nis_result	*res;
590 	char		buf[NIS_MAXNAMELEN + 1];
591 	int		status;
592 	static	char	principal_name[NIS_MAXNAMELEN + 1];
593 
594 	if (uid == 0)
595 		return (nis_local_host());
596 
597 	/* buf is enough to hold the string, no buffer overflow */
598 	(void) sprintf(buf, "[auth_name=%d,auth_type=LOCAL],%s.%s",
599 		uid, CRED_TABLE, directory);
600 
601 	if (buf[strlen(buf)-1] != '.')
602 		strcat(buf, ".");
603 
604 	res = nis_list(buf,
605 		MASTER_ONLY+USE_DGRAM+NO_AUTHINFO+FOLLOW_LINKS,
606 		NULL, NULL);
607 
608 	if (res == NULL) {
609 		(void) fprintf(stderr,
610 			"%s: unable to get result from NIS+ server.",
611 			program_name);
612 		exit(1);
613 	}
614 	switch (res->status) {
615 	case NIS_SUCCESS:
616 		if (res->objects.objects_len > 1) {
617 			/*
618 			 * More than one principal with same uid?
619 			 * something wrong with cred table. Should be unique
620 			 * Warn user and continue.
621 			 */
622 			(void) fprintf(stderr,
623 			"%s: LOCAL entry for %d in directory %s not unique",
624 				program_name, uid, directory);
625 		}
626 		/* make sure we don't run into buffer overflow */
627 		if (strlen(ENTRY_VAL(res->objects.objects_val, 0)) >
628 			sizeof (principal_name))
629 			return (NULL);
630 		strcpy(principal_name,
631 			ENTRY_VAL(res->objects.objects_val, 0));
632 		nis_freeresult(res);
633 		return (principal_name);
634 
635 	case NIS_NOTFOUND:
636 		nis_freeresult(res);
637 		return (NULL);
638 
639 	case NIS_TRYAGAIN :
640 		(void) fprintf(stderr,
641 			"%s: NIS+ server busy, try again later.\n",
642 			program_name);
643 		exit(1);
644 
645 	case NIS_PERMISSION :
646 		(void) fprintf(stderr,
647 			"%s: insufficient permission to update credentials.\n",
648 			program_name);
649 		exit(1);
650 
651 	default:
652 		(void) fprintf(stderr,
653 			"%s: error talking to server, NIS+ error: %s.\n",
654 			program_name, nis_sperrno(res->status));
655 		exit(1);
656 	}
657 	return (NULL);
658 }
659 
660 
661 
662 /* Check whether this principal already has this type of credentials */
663 static nis_error
664 cred_exists(char *nisprinc, char *flavor, char *domain)
665 {
666 	char sname[NIS_MAXNAMELEN+MAXHOSTNAMELEN+64];
667 	nis_result	*res;
668 	nis_error status;
669 
670 	(void) sprintf(sname, "[cname=\"%s\",auth_type=%s],%s.%s",
671 		nisprinc, flavor, CRED_TABLE, domain);
672 	if (sname[strlen(sname)-1] != '.')
673 		strcat(sname, ".");
674 
675 	/* Don't want FOLLOW_PATH here */
676 	res = nis_list(sname,
677 		MASTER_ONLY+USE_DGRAM+NO_AUTHINFO+FOLLOW_LINKS,
678 		NULL, NULL);
679 
680 	status = res->status;
681 	switch (status) {
682 	case NIS_NOTFOUND:
683 		break;
684 	case NIS_TRYAGAIN:
685 		(void) fprintf(stderr,
686 			"%s: NIS+ server busy, try again later.\n",
687 			program_name);
688 		exit(1);
689 	case NIS_PERMISSION:
690 		(void) fprintf(stderr,
691 		"%s: insufficient permission to look at credentials table\n",
692 			program_name);
693 		exit(1);
694 	case NIS_SUCCESS:
695 	case NIS_S_SUCCESS:
696 		break;
697 	default:
698 		(void) fprintf(stderr,
699 			"%s: error looking at cred table, NIS+ error: %s\n",
700 			program_name, nis_sperrno(res->status));
701 		exit(1);
702 	}
703 	nis_freeresult(res);
704 	return (status);
705 }
706 
707 
708 /* Returns 0 if operation fails; 1 if successful. */
709 static
710 int
711 modify_cred_obj(obj, domain)
712 	char *domain;
713 	nis_object *obj;
714 {
715 	int status = 0;
716 	char sname[NIS_MAXNAMELEN+1];
717 	nis_result	*res;
718 
719 	(void) sprintf(sname, "%s.%s", CRED_TABLE, domain);
720 	res = nis_modify_entry(sname, obj, 0);
721 	switch (res->status) {
722 	case NIS_TRYAGAIN :
723 		(void) fprintf(stderr,
724 			"%s: NIS+ server busy, try again later.\n",
725 			program_name);
726 		exit(1);
727 	case NIS_PERMISSION :
728 		(void) fprintf(stderr,
729 			"%s: insufficient permission to update credentials.\n",
730 			program_name);
731 		exit(1);
732 	case NIS_SUCCESS :
733 		status = 1;
734 		break;
735 	default:
736 		(void) fprintf(stderr,
737 			"%s: error modifying credential, NIS+ error: %s.\n",
738 			program_name, nis_sperrno(res->status));
739 		exit(1);
740 	}
741 	nis_freeresult(res);
742 	return (status);
743 }
744 
745 
746 static
747 int
748 add_cred_obj(obj, domain)
749 	char *domain;
750 	nis_object *obj;
751 {
752 	int status = 0;
753 	char sname[NIS_MAXNAMELEN+1];
754 	nis_result	*res;
755 
756 	/* Assume check for cred_exists performed already */
757 
758 	(void) sprintf(sname, "%s.%s", CRED_TABLE, domain);
759 	res = nis_add_entry(sname, obj, 0);
760 	switch (res->status) {
761 	case NIS_TRYAGAIN :
762 		(void) fprintf(stderr,
763 			"%s: NIS+ server busy, try again later.\n",
764 			program_name);
765 		exit(1);
766 	case NIS_PERMISSION :
767 		(void) fprintf(stderr,
768 			"%s: insufficient permission to update credentials.\n",
769 			program_name);
770 		exit(1);
771 	case NIS_SUCCESS :
772 		status = 1;
773 		break;
774 	default:
775 		(void) fprintf(stderr,
776 			"%s: error creating credential, NIS+ error: %s.\n",
777 			program_name, nis_sperrno(res->status));
778 		exit(1);
779 	}
780 	nis_freeresult(res);
781 	return (status);
782 }
783 
784 nis_object *
785 init_entry()
786 {
787 	static nis_object	obj;
788 	static entry_col	cred_data[10];
789 	entry_obj		*eo;
790 
791 	memset((char *)(&obj), 0, sizeof (obj));
792 	memset((char *)(cred_data), 0, sizeof (entry_col) * 10);
793 
794 	obj.zo_name = "cred";
795 	obj.zo_group = "";
796 	obj.zo_ttl = 43200;
797 	obj.zo_data.zo_type = NIS_ENTRY_OBJ;
798 	eo = &(obj.EN_data);
799 	eo->en_type = "cred_tbl";
800 	eo->en_cols.en_cols_val = cred_data;
801 	eo->en_cols.en_cols_len = 5;
802 	cred_data[4].ec_flags |= EN_CRYPT;
803 	return (&obj);
804 }
805 
806 
807 static char	*attrFilter[] = {
808 	"objectclass",
809 	"nispublickey",
810 	"nissecretkey",
811 	(char *)NULL
812 };
813 
814 
815 /* Determines if there is a NisKeyObject objectclass in a given entry */
816 static int
817 ldap_keyobj_exist(ns_ldap_entry_t *entry)
818 {
819 	char		**fattrs;
820 
821 	fattrs = __ns_ldap_getAttr(entry, "objectClass");
822 
823 	if (fattrs == NULL)
824 		return (1);
825 
826 	while (*fattrs) {
827 		if (strcasecmp("NisKeyObject", *fattrs) == 0)
828 			return (1);
829 		fattrs++;
830 	}
831 
832 	return (0);
833 }
834 
835 
836 static char *keyAttrs[] = {
837 	"nispublickey",
838 	"nissecretkey",
839 	NULL
840 };
841 
842 /*
843  * Replace or append new attribute value(s) to an attribute.
844  * Don't care about memory leaks, because program is short running.
845  */
846 
847 static int
848 ldap_attr_mod(ns_ldap_entry_t *entry,
849 	    char *mechname,
850 	    char *public,
851 	    ns_ldap_attr_t **pkeyattrs,
852 	    char *crypt,
853 	    ns_ldap_attr_t **ckeyattrs)
854 {
855 	char		**alist[2];
856 	char		*keys[2];
857 
858 	char		*mechfilter;
859 	int		mechfilterlen;
860 	int		q = 0;
861 	int		i, j;
862 	int		keycount[] = {0, 0};
863 	ns_ldap_attr_t	*attrs;
864 
865 	keys[0] = public;
866 	keys[1] = crypt;
867 
868 	mechfilter = (char *)malloc(strlen(mechname) + 3);
869 	if (mechfilter == NULL)
870 		return (0);
871 	sprintf(mechfilter, "{%s}", mechname);
872 	mechfilterlen = strlen(mechfilter);
873 
874 	for (q = 0; keyAttrs[q] != NULL; q++) {
875 		int		found = 0;
876 
877 		for (i = 0; i < entry->attr_count; i++) {
878 			int		rep = 0;
879 			ns_ldap_attr_t	*attr = entry->attr_pair[i];
880 			char		*name = attr->attrname;
881 			int		count = 0;
882 
883 			if (strcasecmp(keyAttrs[q], name) == 0) {
884 				found++;
885 				count = attr->value_count;
886 		alist[q] = (char **)malloc(sizeof (char *) * (count + 1));
887 				if (alist[q] == NULL)
888 					return (0);
889 				alist[q][attr->value_count] = NULL;
890 				for (j = 0; j < attr->value_count; j++) {
891 					char	*val = attr->attrvalue[j];
892 					if (strncasecmp(val, mechfilter,
893 							mechfilterlen) == 0) {
894 						/* Replace entry */
895 						rep++;
896 						alist[q][j] = keys[q];
897 					} else
898 						alist[q][j] = val;
899 					++keycount[q];
900 				}
901 				if (!rep) {
902 					/* Add entry to list */
903 					alist[q] = (char **)realloc(alist[q],
904 					    sizeof (char *) * (count + 2));
905 					if (alist[q] == NULL)
906 						return (0);
907 					alist[q][attr->value_count + 1] = NULL;
908 					alist[q][attr->value_count] = keys[q];
909 					++keycount[q];
910 				}
911 			}
912 		}
913 		if (!found) {
914 			/* Attribute does not exist, add entry anyways */
915 			alist[q] = (char **)malloc(sizeof (char *) * 2);
916 			if (alist[q] == NULL)
917 				return (0);
918 			alist[q][0] = keys[q];
919 			alist[q][1] = NULL;
920 			++keycount[q];
921 		}
922 	}
923 	if ((attrs = (ns_ldap_attr_t *)calloc(1,
924 			sizeof (ns_ldap_attr_t))) == NULL)
925 		return (0);
926 	attrs->attrname = "nisPublicKey";
927 	attrs->attrvalue = alist[0];
928 	attrs->value_count = keycount[0];
929 	*pkeyattrs = attrs;
930 
931 	if ((attrs = (ns_ldap_attr_t *)calloc(1,
932 			sizeof (ns_ldap_attr_t))) == NULL)
933 		return (0);
934 	attrs->attrname = "nisSecretKey";
935 	attrs->attrvalue = alist[1];
936 	attrs->value_count = keycount[1];
937 	*ckeyattrs = attrs;
938 	return (1);
939 }
940 
941 
942 /*
943  * Do the actual Add or update of attributes in attrs.
944  * The parameter 'update4host' is a flag that tells the function which
945  * DN and password should be used to bind to ldap. If it is an update
946  * for a host (update4host > 0), the two parameters "bindDN" and
947  * "bindPasswd" would be used to bind as the directory manager,
948  * otherwise "dn" and "passwd" would be used to bind as an individual
949  * user.
950  */
951 static void
952 update_ldap_attr(const char *dn,
953 		ns_ldap_attr_t **attrs,
954 		const char *passwd,
955 		int add,
956 		int update4host,
957 		const char *bindDN,
958 		const char *bindPasswd)
959 {
960 	int		ldaprc;
961 	int		authstried = 0;
962 	char		*msg;
963 	char		*ldap_pw;
964 	char		**certpath = NULL;
965 	ns_auth_t	**app;
966 	ns_auth_t	**authpp = NULL;
967 	ns_auth_t	*authp = NULL;
968 	ns_cred_t	*credp;
969 	ns_ldap_error_t	*errorp = NULL;
970 	int		status;
971 
972 	if ((credp = (ns_cred_t *)calloc(1, sizeof (ns_cred_t))) == NULL) {
973 		fprintf(stderr, "Can not allocate cred buffer.\n");
974 		goto out;
975 	}
976 
977 	/*
978 	 * if this is an update for host, use the bindDN from the
979 	 * command prompt, otherwise use user's DN directly.
980 	 */
981 	if (update4host)
982 		credp->cred.unix_cred.userID = strdup(bindDN);
983 	else
984 		credp->cred.unix_cred.userID = strdup(dn);
985 
986 	if (credp->cred.unix_cred.userID == NULL) {
987 		fprintf(stderr, "Memory allocation failure (userID)\n");
988 		goto out;
989 	}
990 
991 	if (update4host) {
992 		credp->cred.unix_cred.passwd = strdup(bindPasswd);
993 	} else {
994 		if (passwd)
995 			credp->cred.unix_cred.passwd = strdup(passwd);
996 		else {
997 			/* Make sure a valid password is received. */
998 			status = get_ldap_bindPassword(&ldap_pw);
999 
1000 			if (status != PROMPTGET_SUCCESS) {
1001 				if (!ldap_pw)
1002 					free(ldap_pw);
1003 				goto out;
1004 			}
1005 			credp->cred.unix_cred.passwd = ldap_pw;
1006 		}
1007 	}
1008 
1009 	if (credp->cred.unix_cred.passwd == NULL) {
1010 		fprintf(stderr, "Memory allocation failure (passwd)\n");
1011 		goto out;
1012 	}
1013 
1014 	/* get host certificate path, if one is configured */
1015 	if (__ns_ldap_getParam(NS_LDAP_HOST_CERTPATH_P,
1016 		(void ***)&certpath, &errorp) != NS_LDAP_SUCCESS)
1017 		goto out;
1018 
1019 	if (certpath && *certpath)
1020 		credp->hostcertpath = *certpath;
1021 
1022 	/* Load the service specific authentication method */
1023 	if (__ns_ldap_getServiceAuthMethods("keyserv", &authpp, &errorp) !=
1024 		NS_LDAP_SUCCESS)
1025 		goto out;
1026 
1027 	/*
1028 	 * if authpp is null, there is no serviceAuthenticationMethod
1029 	 * try default authenticationMethod
1030 	 */
1031 	if (authpp == NULL) {
1032 		if (__ns_ldap_getParam(NS_LDAP_AUTH_P, (void ***)&authpp,
1033 			&errorp) != NS_LDAP_SUCCESS)
1034 			goto out;
1035 	}
1036 
1037 	/*
1038 	 * if authpp is still null, then can not authenticate, log
1039 	 * error message and return error
1040 	 */
1041 	if (authpp == NULL) {
1042 		fprintf(stderr, "No LDAP authentication method configured.\n"
1043 			" configured.\n");
1044 		goto out;
1045 	}
1046 
1047 	/*
1048 	 * Walk the array and try all authentication methods in order except
1049 	 * for "none".
1050 	 */
1051 	for (app = authpp; *app; app++) {
1052 		authp = *app;
1053 		/* what about disabling other mechanisms? "tls:sasl/EXTERNAL" */
1054 		if (authp->type == NS_LDAP_AUTH_NONE)
1055 			continue;
1056 		authstried++;
1057 		credp->auth.type = authp->type;
1058 		credp->auth.tlstype = authp->tlstype;
1059 		credp->auth.saslmech = authp->saslmech;
1060 		credp->auth.saslopt = authp->saslopt;
1061 
1062 		if (add == TRUE)
1063 			ldaprc = __ns_ldap_addAttr("publickey", dn,
1064 				(const ns_ldap_attr_t * const *)attrs,
1065 				credp, NULL, &errorp);
1066 		else
1067 			ldaprc = __ns_ldap_repAttr("publickey", dn,
1068 				(const ns_ldap_attr_t * const *)attrs,
1069 				credp, NULL, &errorp);
1070 		if (ldaprc == NS_LDAP_SUCCESS) {
1071 			/* clean up ns_cred_t structure in memory */
1072 			if (credp != NULL)
1073 				(void) __ns_ldap_freeCred(&credp);
1074 			return;
1075 		}
1076 
1077 		/* XXX add checking for cases of authentication errors */
1078 		if ((ldaprc == NS_LDAP_INTERNAL) &&
1079 			((errorp->status == LDAP_INAPPROPRIATE_AUTH) ||
1080 			(errorp->status == LDAP_INVALID_CREDENTIALS))) {
1081 			fprintf(stderr, "LDAP authentication failed.\n");
1082 			goto out;
1083 		}
1084 	}
1085 	if (authstried == 0)
1086 		fprintf(stderr, "No legal authentication method configured.\n");
1087 
1088 out:
1089 	/* clean up ns_cred_t structure in memory */
1090 	if (credp != NULL) {
1091 		(void) __ns_ldap_freeCred(&credp);
1092 	}
1093 
1094 	if (errorp) {
1095 		__ns_ldap_err2str(errorp->status, &msg);
1096 		fprintf(stderr, "LDAP error: %s.\n", msg);
1097 	}
1098 	fprintf(stderr, "%s: key-pair(s) unchanged.\n", program_name);
1099 	exit(1);
1100 }
1101 
1102 
1103 /*
1104  * Update LDAP nisplublickey entry with new key information via SLDAP.
1105  * Free and clean up memory that stores credential data soon after
1106  * they are not used or an error comes up.
1107  */
1108 int
1109 ldap_update(char *mechname,
1110 	    char *netname,
1111 	    char *public,
1112 	    char *crypt,
1113 	    char *passwd)
1114 {
1115 	char		*netnamecpy;
1116 	char		*id;
1117 	char		*domain;
1118 	char		*dn;
1119 	char		*db;
1120 	char		*filter;
1121 	ns_ldap_error_t	*errorp;
1122 	char		*pkeyatval, *ckeyatval;
1123 	ns_ldap_result_t	*res;
1124 	ns_ldap_attr_t	*pattrs, *cattrs;
1125 	int		update4host = FALSE;
1126 	char		*bindDN = NULL;
1127 	char		*bindPasswd = NULL;
1128 	int		status;
1129 
1130 	/* Generate DN */
1131 	if ((netnamecpy = strdup(netname)) == NULL)
1132 		return (0);
1133 	if (((id = strchr(netnamecpy, '.')) == NULL) ||
1134 	    ((domain = strchr(netnamecpy, '@')) == NULL))
1135 		return (0);
1136 	else {
1137 		*domain++ = '\0';
1138 		*id++ = '\0';
1139 
1140 		id = strdup(id);
1141 		if (id == NULL) {
1142 			free(netnamecpy);
1143 			fprintf(stderr, "LDAP memory error (id)\n");
1144 			return (0);
1145 		}
1146 		domain = strdup(domain);
1147 		if (domain == NULL) {
1148 			free(netnamecpy);
1149 			free(id);
1150 			fprintf(stderr, "LDAP memory error (domain)\n");
1151 			return (0);
1152 		}
1153 		free(netnamecpy);
1154 	}
1155 
1156 	if (isdigit(*id)) {
1157 		/* We be user. */
1158 		__ns_ldap_uid2dn(id, &dn, NULL, &errorp);
1159 		if (dn == NULL) {
1160 			fprintf(stderr, "Could not obtain LDAP dn\n");
1161 			fprintf(stderr, "%s: key-pair(s) unchanged.\n",
1162 				program_name);
1163 			exit(1);
1164 		}
1165 		db = "passwd";
1166 		filter = (char *)malloc(strlen(id) + 13);
1167 		if (filter)
1168 			sprintf(filter, "(uidnumber=%s)", id);
1169 		else {
1170 			fprintf(stderr, "Can not allocate filter buffer.\n");
1171 			fprintf(stderr, "%s: key-pair(s) unchanged.\n",
1172 				program_name);
1173 			exit(1);
1174 		}
1175 	} else {
1176 		/* We be host. */
1177 		update4host = TRUE;
1178 
1179 		__ns_ldap_host2dn(id, NULL, &dn, NULL, &errorp);
1180 		if (dn == NULL) {
1181 			fprintf(stderr, "Could not obtain LDAP dn\n");
1182 			fprintf(stderr, "%s: key-pair(s) unchanged.\n",
1183 				program_name);
1184 			exit(1);
1185 		}
1186 
1187 		db = "hosts";
1188 		filter = (char *)malloc(strlen(id) + 6);
1189 		if (filter)
1190 			sprintf(filter, "(cn=%s)", id);
1191 		else {
1192 			fprintf(stderr, "Can not allocate filter buffer.\n");
1193 			fprintf(stderr, "%s: key-pair(s) unchanged.\n",
1194 				program_name);
1195 			exit(1);
1196 		}
1197 
1198 		/* Prompt for ldap bind DN for entry udpates */
1199 		status = get_ldap_bindDN(&bindDN);
1200 
1201 		if (status != PROMPTGET_SUCCESS) {
1202 			FREE_CREDINFO(bindDN);
1203 			fprintf(stderr,
1204 				"Failed to get a valid LDAP bind DN.\n"
1205 				"%s: key-pair(s) unchanged.\n",
1206 				program_name);
1207 			exit(1);
1208 		}
1209 
1210 		/* Prompt for ldap bind password */
1211 		status = get_ldap_bindPassword(&bindPasswd);
1212 
1213 		if (status != PROMPTGET_SUCCESS) {
1214 			FREE_CREDINFO(bindPasswd);
1215 			FREE_CREDINFO(bindDN);
1216 
1217 			fprintf(stderr,
1218 				"Failed to get a valid LDAP bind password."
1219 				"\n%s: key-pair(s) unchanged.\n",
1220 				program_name);
1221 			exit(1);
1222 		}
1223 	}
1224 
1225 	/* Construct attribute values */
1226 	pkeyatval = (char *)malloc(strlen(mechname) + strlen(public) + 3);
1227 	if (pkeyatval == NULL) {
1228 		FREE_CREDINFO(bindPasswd);
1229 		FREE_CREDINFO(bindDN);
1230 		fprintf(stderr, "LDAP memory error (pkeyatval)\n");
1231 		fprintf(stderr, "%s: key-pair(s) unchanged.\n", program_name);
1232 		exit(1);
1233 	}
1234 	sprintf(pkeyatval, "{%s}%s", mechname, public);
1235 	ckeyatval = (char *)malloc(strlen(mechname) + strlen(crypt) + 3);
1236 	if (ckeyatval == NULL) {
1237 		FREE_CREDINFO(pkeyatval);
1238 		FREE_CREDINFO(bindPasswd);
1239 		FREE_CREDINFO(bindDN);
1240 		fprintf(stderr, "LDAP memory error (pkeyatval)\n");
1241 		fprintf(stderr, "%s: key-pair(s) unchanged.\n", program_name);
1242 		exit(1);
1243 	}
1244 	sprintf(ckeyatval, "{%s}%s", mechname, crypt);
1245 
1246 	/* Does entry exist? */
1247 	if ((__ns_ldap_list(db, filter, NULL, (const char **)attrFilter,
1248 			    NULL, 0, &res, &errorp,
1249 			    NULL, NULL) == NS_LDAP_SUCCESS) && res == NULL) {
1250 		FREE_CREDINFO(ckeyatval);
1251 		FREE_CREDINFO(pkeyatval);
1252 		FREE_CREDINFO(bindPasswd);
1253 		FREE_CREDINFO(bindDN);
1254 		fprintf(stderr, "LDAP entry does not exist.\n");
1255 		fprintf(stderr, "%s: key-pair(s) unchanged.\n", program_name);
1256 		exit(1);
1257 	}
1258 
1259 	/* Entry exists, modify attributes for public and secret keys */
1260 
1261 	/* Is there a NisKeyObject in entry? */
1262 	if (!ldap_keyobj_exist(&res->entry[0])) {
1263 		/* Add NisKeyObject objectclass and the keys */
1264 		char	**newattr;
1265 		ns_ldap_attr_t	*attrs[4]; /* objectclass, pk, sk, NULL */
1266 
1267 		/* set objectclass */
1268 		newattr = (char **)calloc(2, sizeof (char *));
1269 		newattr[0] = "NisKeyObject";
1270 		newattr[1] = NULL;
1271 		if ((attrs[0] = (ns_ldap_attr_t *)calloc(1,
1272 				sizeof (ns_ldap_attr_t))) == NULL) {
1273 			FREE_CREDINFO(ckeyatval);
1274 			FREE_CREDINFO(pkeyatval);
1275 			FREE_CREDINFO(bindPasswd);
1276 			FREE_CREDINFO(bindDN);
1277 			fprintf(stderr, "Memory allocation failed\n");
1278 			fprintf(stderr, "%s: key-pair(s) unchanged.\n",
1279 				program_name);
1280 			exit(1);
1281 		}
1282 		attrs[0]->attrname = "objectClass";
1283 		attrs[0]->attrvalue = newattr;
1284 		attrs[0]->value_count = 1;
1285 
1286 		/* set publickey */
1287 		newattr = (char **)calloc(2, sizeof (char *));
1288 		newattr[0] = pkeyatval;
1289 		newattr[1] = NULL;
1290 		if ((attrs[1] = (ns_ldap_attr_t *)calloc(1,
1291 				sizeof (ns_ldap_attr_t))) == NULL) {
1292 			FREE_CREDINFO(ckeyatval);
1293 			FREE_CREDINFO(pkeyatval);
1294 			FREE_CREDINFO(bindPasswd);
1295 			FREE_CREDINFO(bindDN);
1296 			fprintf(stderr, "Memory allocation failed\n");
1297 			fprintf(stderr, "%s: key-pair(s) unchanged.\n",
1298 				program_name);
1299 			exit(1);
1300 		}
1301 		attrs[1]->attrname = "nisPublicKey";
1302 		attrs[1]->attrvalue = newattr;
1303 		attrs[1]->value_count = 1;
1304 
1305 		/* set privatekey */
1306 		newattr = (char **)calloc(2, sizeof (char *));
1307 		newattr[0] = ckeyatval;
1308 		newattr[1] = NULL;
1309 		if ((attrs[2] = (ns_ldap_attr_t *)calloc(1,
1310 				sizeof (ns_ldap_attr_t))) == NULL) {
1311 			FREE_CREDINFO(ckeyatval);
1312 			FREE_CREDINFO(pkeyatval);
1313 			FREE_CREDINFO(bindPasswd);
1314 			FREE_CREDINFO(bindDN);
1315 			fprintf(stderr, "Memory allocation failed\n");
1316 			fprintf(stderr, "%s: key-pair(s) unchanged.\n",
1317 				program_name);
1318 			exit(1);
1319 		}
1320 		attrs[2]->attrname = "nisSecretKey";
1321 		attrs[2]->attrvalue = newattr;
1322 		attrs[2]->value_count = 1;
1323 
1324 		/* terminator */
1325 		attrs[3] = NULL;
1326 
1327 		update_ldap_attr(dn, attrs, passwd, TRUE, update4host,
1328 				bindDN, bindPasswd);
1329 	} else {
1330 		/* object class already exists, replace keys */
1331 		ns_ldap_attr_t	*attrs[4]; /* objectclass, pk, sk, NULL */
1332 
1333 		if (!ldap_attr_mod(&res->entry[0], mechname,
1334 				pkeyatval, &pattrs,
1335 				ckeyatval, &cattrs)) {
1336 			FREE_CREDINFO(ckeyatval);
1337 			FREE_CREDINFO(pkeyatval);
1338 			FREE_CREDINFO(bindPasswd);
1339 			FREE_CREDINFO(bindDN);
1340 			fprintf(stderr,
1341 				"Could not generate LDAP attribute list.\n");
1342 			fprintf(stderr,
1343 				"%s: key-pair(s) unchanged.\n", program_name);
1344 			exit(1);
1345 		}
1346 
1347 		attrs[0] = pattrs;
1348 		attrs[1] = cattrs;
1349 		attrs[2] = NULL;
1350 
1351 		update_ldap_attr(dn, attrs, passwd, FALSE, update4host,
1352 				bindDN, bindPasswd);
1353 	}
1354 
1355 	FREE_CREDINFO(ckeyatval);
1356 	FREE_CREDINFO(pkeyatval);
1357 	FREE_CREDINFO(bindPasswd);
1358 	FREE_CREDINFO(bindDN);
1359 
1360 	return (0);
1361 }
1362 
1363 
1364 /* Returns 0 if successful; -1 if failure. (expected by setpublicmap). */
1365 
1366 int
1367 nisplus_update(char *netname, char *public, char *secret, nis_name nis_princ)
1368 {
1369 	nis_object	*obj = init_entry();
1370 	char	*domain, *netdomain;
1371 	char	netdomainaux[MAXHOSTNAMELEN + 1];
1372 	int status, addition;
1373 
1374 	/*
1375 	 * we take the domain given in the netname & the principal
1376 	 * name if they match otherwise the local domain.
1377 	 */
1378 
1379 	netdomain = (char *)strchr(netname, '@');
1380 	if (! netdomain) {
1381 		(void) fprintf(stderr, "%s: invalid netname: '%s'. \n",
1382 			program_name, netname);
1383 		return (0);
1384 	}
1385 	netdomain++; /* skip '@' */
1386 	/* make sure we don't run into buffer overflow */
1387 	if (strlen(netdomain) > sizeof (netdomainaux))
1388 		return (0);
1389 	strcpy(netdomainaux, netdomain);
1390 	if (netdomainaux[strlen(netdomainaux) - 1] != '.')
1391 		strcat(netdomainaux, ".");
1392 
1393 	domain = nis_domain_of(nis_princ);
1394 	if (strcasecmp(domain, netdomainaux) != 0)
1395 		domain = nis_local_directory();
1396 
1397 	if (sanity_checks(nis_princ, netname, domain) == 0)
1398 		return (-1);
1399 
1400 	addition = (cred_exists(nis_princ, "DES", domain) == NIS_NOTFOUND);
1401 
1402 	/* Now we have a key pair, build up the cred entry */
1403 	ENTRY_VAL(obj, 0) = nis_princ;
1404 	ENTRY_LEN(obj, 0) = strlen(nis_princ) + 1;
1405 
1406 	ENTRY_VAL(obj, 1) = "DES";
1407 	ENTRY_LEN(obj, 1) = 4;
1408 
1409 	ENTRY_VAL(obj, 2) = netname;
1410 	ENTRY_LEN(obj, 2) = strlen(netname) + 1;
1411 
1412 	ENTRY_VAL(obj, 3) = public;
1413 	ENTRY_LEN(obj, 3) = strlen(public) + 1;
1414 
1415 	ENTRY_VAL(obj, 4) = secret;
1416 	ENTRY_LEN(obj, 4) = strlen(secret) + 1;
1417 
1418 	if (addition) {
1419 		obj->zo_owner = nis_princ;
1420 		obj->zo_group = nis_local_group();
1421 		obj->zo_domain = domain;
1422 		/* owner: r, group: rmcd */
1423 		obj->zo_access = ((NIS_READ_ACC<<16)|
1424 				(NIS_READ_ACC|NIS_MODIFY_ACC|NIS_CREATE_ACC|
1425 					NIS_DESTROY_ACC)<<8);
1426 		status = add_cred_obj(obj, domain);
1427 	} else {
1428 		obj->EN_data.en_cols.en_cols_val[3].ec_flags |= EN_MODIFIED;
1429 		obj->EN_data.en_cols.en_cols_val[4].ec_flags |= EN_MODIFIED;
1430 		status = modify_cred_obj(obj, domain);
1431 	}
1432 
1433 	return (status == 1 ? 0 : -1);
1434 }
1435