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 #include <rpcsvc/nispasswd.h> 57 58 #define MAXMAPNAMELEN 256 59 #define MAXPASSWD 256 /* max significant characters in password */ 60 61 #define PK_FILES 1 62 #define PK_YP 2 63 #define PK_NISPLUS 3 64 #define PK_LDAP 4 65 66 extern int optind; 67 extern char *optarg; 68 extern char *get_nisplus_principal(); 69 extern int __getnetnamebyuid(); 70 extern int self_check(char *name); 71 72 #define local_host(host_name) self_check(host_name) 73 74 char *program_name; 75 int pk_database; 76 static char *get_password(); 77 static char *basename(); 78 static char SHELL[] = "/bin/sh"; 79 static char YPDBPATH[] = "/var/yp"; 80 static char PKMAP[] = "publickey.byname"; 81 static char UPDATEFILE[] = "updaters"; 82 static char PKFILE[] = "/etc/publickey"; 83 static void usage(void); 84 85 int 86 main(int argc, char *argv[]) 87 { 88 char name[MAXNETNAMELEN + 1]; 89 char public[HEXKEYBYTES + 1]; 90 char secret[HEXKEYBYTES + 1]; 91 char crypt1[HEXKEYBYTES + KEYCHECKSUMSIZE + 1]; 92 int status; 93 char *pass, *target_host = NULL, 94 *username = NULL, *pk_service = NULL; 95 char short_pass[DESCREDPASSLEN + 1]; 96 struct passwd *pw; 97 NCONF_HANDLE *nc_handle; 98 struct netconfig *nconf; 99 struct nd_hostserv service; 100 struct nd_addrlist *addrs; 101 bool_t validhost; 102 uid_t uid; 103 int c; 104 char *nprinc = NULL; /* nisplus principal name */ 105 char host_pname[NIS_MAXNAMELEN]; 106 107 program_name = argv[0]; 108 while ((c = getopt(argc, argv, "s:u:h:")) != -1) { 109 switch (c) { 110 case 's': 111 if (pk_service == NULL) 112 pk_service = optarg; 113 else 114 usage(); 115 break; 116 case 'u': 117 if (username || target_host) 118 usage(); 119 username = optarg; 120 break; 121 case 'h': 122 if (username || target_host) 123 usage(); 124 target_host = optarg; 125 break; 126 default: 127 usage(); 128 } 129 } 130 131 if (optind < argc || (username == 0 && target_host == 0)) { 132 usage(); 133 } 134 135 if ((pk_database = get_pk_source(pk_service)) == 0) 136 usage(); 137 138 if (geteuid() != 0) { 139 (void) fprintf(stderr, "Must be superuser to run %s\n", 140 program_name); 141 exit(1); 142 } 143 144 if (username) { 145 pw = getpwnam(username); 146 if (pw == NULL) { 147 (void) fprintf(stderr, "%s: unknown user: '%s'\n", 148 program_name, username); 149 exit(1); 150 } 151 uid = pw->pw_uid; 152 if (uid == 0) { 153 if (! getnetname(name)) { 154 (void) fprintf(stderr, 155 "%s: could not get the equivalent netname for %s\n", 156 program_name, username); 157 usage(); 158 } 159 if (pk_database == PK_NISPLUS) 160 target_host = nis_local_host(); 161 else { 162 if (gethostname(host_pname, NIS_MAXNAMELEN) 163 < 0) { 164 (void) fprintf(stderr, 165 "%s: could not get the hostname for %s\n", 166 program_name, username); 167 usage(); 168 } 169 target_host = host_pname; 170 } 171 } 172 if (__getnetnamebyuid(name, uid) == 0) { 173 (void) fprintf(stderr, 174 "%s: could not get the equivalent netname for %s\n", 175 program_name, username); 176 usage(); 177 } 178 if (pk_database == PK_NISPLUS) 179 nprinc = get_nisplus_principal(nis_local_directory(), 180 uid); 181 } else { 182 /* -h hostname option */ 183 service.h_host = target_host; 184 service.h_serv = NULL; 185 validhost = FALSE; 186 /* verify if this is a valid hostname */ 187 nc_handle = setnetconfig(); 188 if (nc_handle == NULL) { 189 /* fails to open netconfig file */ 190 (void) fprintf(stderr, 191 "%s: failed in routine setnetconfig()\n", 192 program_name); 193 exit(2); 194 } 195 while (nconf = getnetconfig(nc_handle)) { 196 /* check to see if hostname exists for this transport */ 197 if ((netdir_getbyname(nconf, &service, &addrs) == 0) && 198 (addrs->n_cnt != 0)) { 199 /* at least one valid address */ 200 validhost = TRUE; 201 break; 202 } 203 } 204 endnetconfig(nc_handle); 205 if (!validhost) { 206 (void) fprintf(stderr, "%s: unknown host: %s\n", 207 program_name, target_host); 208 exit(1); 209 } 210 (void) host2netname(name, target_host, (char *)NULL); 211 if (pk_database == PK_NISPLUS) { 212 if (target_host[strlen(target_host) - 1] != '.') { 213 sprintf(host_pname, "%s.%s", 214 target_host, nis_local_directory()); 215 nprinc = host_pname; 216 } else 217 nprinc = target_host; 218 } 219 uid = 0; 220 } 221 222 (void) fprintf(stdout, "Adding new key for %s.\n", name); 223 pass = get_password(uid, target_host, username); 224 225 if (pass == NULL) 226 exit(1); 227 228 (void) strlcpy(short_pass, pass, sizeof (short_pass)); 229 (void) __gen_dhkeys(public, secret, short_pass); 230 231 (void) memcpy(crypt1, secret, HEXKEYBYTES); 232 (void) memcpy(crypt1 + HEXKEYBYTES, secret, KEYCHECKSUMSIZE); 233 crypt1[HEXKEYBYTES + KEYCHECKSUMSIZE] = 0; 234 xencrypt(crypt1, short_pass); 235 236 if (status = setpublicmap(name, public, crypt1, pk_database, 237 nprinc, short_pass)) { 238 switch (pk_database) { 239 case PK_YP: 240 (void) fprintf(stderr, 241 "%s: unable to update NIS database (%u): %s\n", 242 program_name, status, 243 yperr_string(status)); 244 break; 245 case PK_FILES: 246 (void) fprintf(stderr, 247 "%s: hence, unable to update publickey database\n", 248 program_name); 249 break; 250 case PK_NISPLUS: 251 (void) fprintf(stderr, 252 "%s: unable to update nisplus database\n", 253 program_name); 254 break; 255 default: 256 (void) fprintf(stderr, 257 "%s: could not update unknown database: %d\n", 258 program_name, pk_database); 259 } 260 exit(1); 261 } 262 return (0); 263 } 264 265 /* 266 * Set the entry in the public key file 267 */ 268 int 269 setpublicmap(name, public, secret, database, nis_princ, pw) 270 int database; 271 char *name; 272 char *public; 273 char *secret; 274 nis_name nis_princ; 275 char *pw; 276 { 277 char pkent[HEXKEYBYTES + HEXKEYBYTES + KEYCHECKSUMSIZE + 2]; 278 char *domain = NULL; 279 char *master = NULL; 280 char hostname[MAXHOSTNAMELEN+1]; 281 282 (void) sprintf(pkent, "%s:%s", public, secret); 283 switch (database) { 284 case PK_YP: 285 /* check that we're on the master server */ 286 (void) yp_get_default_domain(&domain); 287 if (yp_master(domain, PKMAP, &master) != 0) { 288 (void) fprintf(stderr, 289 "%s: cannot find master of NIS publickey database\n", 290 program_name); 291 exit(1); 292 } 293 if (gethostname(hostname, MAXHOSTNAMELEN) < 0) { 294 (void) fprintf(stderr, 295 "%s: cannot find my own host name\n", 296 program_name); 297 exit(1); 298 } 299 if (strcmp(master, hostname) != 0) { 300 (void) fprintf(stderr, 301 "%s: can only be used on NIS master machine '%s'\n", 302 program_name, master); 303 exit(1); 304 } 305 306 if (chdir(YPDBPATH) < 0) { 307 (void) fprintf(stderr, "%s: cannot chdir to %s", 308 program_name, YPDBPATH); 309 } 310 (void) fprintf(stdout, 311 "Please wait for the database to get updated ...\n"); 312 return (mapupdate(name, PKMAP, YPOP_STORE, pkent)); 313 case PK_FILES: 314 return (localupdate(name, PKFILE, YPOP_STORE, pkent)); 315 case PK_NISPLUS: 316 return (nisplus_update(name, public, secret, nis_princ)); 317 case PK_LDAP: 318 return (ldap_update("dh192-0", name, public, secret, pw)); 319 default: 320 break; 321 } 322 return (1); 323 } 324 325 void 326 usage(void) 327 { 328 (void) fprintf(stderr, 329 "usage:\t%s -u username [-s ldap | nisplus | nis | files]\n", 330 program_name); 331 (void) fprintf(stderr, 332 "\t%s -h hostname [-s ldap | nisplus | nis | files]\n", 333 program_name); 334 exit(1); 335 } 336 337 /* 338 * The parameters passed into the routine get_password and the 339 * return values are as follows: 340 * If the -h flag was specified on the command line: 341 * (a) username is null 342 * (b) target_host is non-null 343 * (c) uid is 0 344 * (d) the login password of root on target_host is returned 345 * 346 * If the -u flag was specified on the command line: 347 * (a) username is non-null 348 * (b) target_host is null in all cases except when username is root; 349 * in that case target_host is set to the local host 350 * (c) uid is set to the username's uid 351 * (d) the login password of the user <username> is returned 352 */ 353 static char * 354 get_password(uid, target_host, username) 355 uid_t uid; 356 char *target_host; 357 char *username; 358 { 359 static char password[MAXPASSWD+1]; 360 char prompt[MAXPASSWD+MAXHOSTNAMELEN+64]; 361 char *encrypted_password, 362 *login_password = NULL, 363 *pass = NULL; 364 struct passwd *pw; 365 struct spwd *spw; 366 367 if ((username != 0) || 368 (target_host != 0) && (local_host(target_host))) { 369 370 /* 371 * "-u username" or "-h localhost" was specified on the 372 * command line 373 */ 374 375 pw = getpwuid(uid); 376 377 if (! pw) { 378 (void) fprintf(stderr, 379 "%s: unable to locate password record for uid %d\n", 380 program_name, uid); 381 return (0); 382 } 383 spw = getspnam(pw->pw_name); 384 if (spw) 385 login_password = spw->sp_pwdp; 386 387 if (! login_password || (strlen(login_password) == 0)) { 388 (void) fprintf(stderr, 389 "%s: unable to locate shadow password record for %s\n", 390 program_name, pw->pw_name); 391 return (0); 392 } 393 394 if (uid == 0) { 395 (void) sprintf(prompt, "Enter local root login password:"); 396 } else 397 (void) sprintf(prompt, "Enter %s's login password:", 398 pw->pw_name); 399 400 pass = getpassphrase(prompt); 401 if (pass && strlen(pass) == 0) { 402 (void) fprintf(stderr, "%s: Invalid password.\n", 403 program_name); 404 return (0); 405 } 406 strcpy(password, pass); 407 encrypted_password = crypt(password, login_password); 408 409 /* Verify that password supplied matches login password */ 410 if (strcmp(encrypted_password, login_password) != 0) { 411 /* 412 * Give another chance for typo 413 */ 414 pass = getpassphrase("Please retype password:"); 415 if (pass && strlen(pass) == 0) { 416 (void) fprintf(stderr, "%s: Invalid password.\n", 417 program_name); 418 return (0); 419 } 420 strcpy(password, pass); 421 encrypted_password = crypt(password, login_password); 422 if (strcmp(encrypted_password, login_password) != 0) { 423 (void) fprintf(stderr, 424 "%s: ERROR, invalid password.\n", 425 program_name); 426 return (0); 427 } 428 } 429 } else { 430 /* 431 * "-h remotehost" was specified on the command line 432 * 433 * Since we cannot verify the root password of the remote 434 * host we have to trust what the user inputs. We can, 435 * however, reduce the possibility of an error by prompting 436 * the user to enter the target host's password twice and 437 * comparing those two. We can also authenticate the 438 * user to be root by checking the real uid. 439 */ 440 441 if (getuid() != 0) { 442 (void) fprintf(stderr, "Must be superuser to run %s\n", 443 program_name); 444 return (0); 445 } 446 447 (void) sprintf(prompt, 448 "Enter %s's root login password:", 449 target_host); 450 pass = getpassphrase(prompt); 451 if (!pass) { 452 (void) fprintf(stderr, 453 "%s: getpass failed.\n", 454 program_name); 455 return (0); 456 } 457 if (!*pass) { 458 (void) fprintf(stderr, 459 "%s: Invalid root password.\n", 460 program_name); 461 return (0); 462 } 463 strcpy(password, pass); 464 465 /* 466 * Now re-enter the password and compare it to the 467 * one just read. 468 */ 469 (void) sprintf(prompt, 470 "Please confirm %s's root login password:", 471 target_host); 472 pass = getpassphrase(prompt); 473 if (!pass) { 474 (void) fprintf(stderr, 475 "%s: getpass failed.\n", 476 program_name); 477 return (0); 478 } 479 if (!*pass) { 480 (void) fprintf(stderr, 481 "%s: Invalid root password.\n", 482 program_name); 483 return (0); 484 } 485 if (strcmp(pass, password) != 0) { 486 (void) fprintf(stderr, 487 "%s: Password Incorrect.\n", 488 program_name); 489 return (0); 490 } 491 } 492 493 return (password); 494 } 495