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