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 2003 Sun Microsystems, Inc. All rights reserved. 24 * Use is subject to license terms. 25 */ 26 27 /* Copyright (c) 1984, 1986, 1987, 1988, 1989 AT&T */ 28 /* All Rights Reserved */ 29 30 /* 31 * University Copyright- Copyright (c) 1982, 1986, 1988 32 * The Regents of the University of California 33 * All Rights Reserved 34 * 35 * University Acknowledgment- Portions of this document are derived from 36 * software developed by the University of California, Berkeley, and its 37 * contributors. 38 */ 39 40 #pragma ident "%Z%%M% %I% %E% SMI" 41 42 /* 43 * Administrative tool to add a new user to the publickey database 44 */ 45 #include <stdio.h> 46 #include <stdlib.h> 47 #include <rpc/rpc.h> 48 #include <rpc/key_prot.h> 49 #include <rpcsvc/ypclnt.h> 50 #include <sys/wait.h> 51 #include <netdb.h> 52 #include <pwd.h> 53 #include <shadow.h> 54 #include <crypt.h> 55 #include <string.h> 56 #include <sys/resource.h> 57 #include <netdir.h> 58 #include <rpcsvc/nis.h> 59 60 #define MAXMAPNAMELEN 256 61 #define MAXPASSWD 256 /* max significant characters in password */ 62 63 #define PK_FILES 1 64 #define PK_YP 2 65 #define PK_NISPLUS 3 66 #define PK_LDAP 4 67 68 extern int optind; 69 extern char *optarg; 70 extern char *get_nisplus_principal(); 71 extern int __getnetnamebyuid(); 72 extern int self_check(char *name); 73 74 #define local_host(host_name) self_check(host_name) 75 76 char *program_name; 77 int pk_database; 78 static char *get_password(); 79 static char *basename(); 80 static char SHELL[] = "/bin/sh"; 81 static char YPDBPATH[] = "/var/yp"; 82 static char PKMAP[] = "publickey.byname"; 83 static char UPDATEFILE[] = "updaters"; 84 static char PKFILE[] = "/etc/publickey"; 85 86 main(argc, argv) 87 int argc; 88 char *argv[]; 89 { 90 char name[MAXNETNAMELEN + 1]; 91 char public[HEXKEYBYTES + 1]; 92 char secret[HEXKEYBYTES + 1]; 93 char crypt1[HEXKEYBYTES + KEYCHECKSUMSIZE + 1]; 94 int status; 95 char *pass, *target_host = NULL, 96 *username = NULL, *pk_service = NULL; 97 struct passwd *pw; 98 NCONF_HANDLE *nc_handle; 99 struct netconfig *nconf; 100 struct nd_hostserv service; 101 struct nd_addrlist *addrs; 102 bool_t validhost; 103 uid_t uid; 104 int c; 105 char *nprinc = NULL; /* nisplus principal name */ 106 char host_pname[NIS_MAXNAMELEN]; 107 108 program_name = argv[0]; 109 while ((c = getopt(argc, argv, "s:u:h:")) != -1) { 110 switch (c) { 111 case 's': 112 if (pk_service == NULL) 113 pk_service = optarg; 114 else 115 usage(); 116 break; 117 case 'u': 118 if (username || target_host) 119 usage(); 120 username = optarg; 121 break; 122 case 'h': 123 if (username || target_host) 124 usage(); 125 target_host = optarg; 126 break; 127 default: 128 usage(); 129 } 130 } 131 132 if (optind < argc || (username == 0 && target_host == 0)) { 133 usage(); 134 } 135 136 if ((pk_database = get_pk_source(pk_service)) == 0) 137 usage(); 138 139 if (geteuid() != 0) { 140 (void) fprintf(stderr, "Must be superuser to run %s\n", 141 program_name); 142 exit(1); 143 } 144 145 if (username) { 146 pw = getpwnam(username); 147 if (pw == NULL) { 148 (void) fprintf(stderr, "%s: unknown user: '%s'\n", 149 program_name, username); 150 exit(1); 151 } 152 uid = pw->pw_uid; 153 if (uid == 0) { 154 if (! getnetname(name)) { 155 (void) fprintf(stderr, 156 "%s: could not get the equivalent netname for %s\n", 157 program_name, username); 158 usage(); 159 } 160 if (pk_database == PK_NISPLUS) 161 target_host = nis_local_host(); 162 else { 163 if (gethostname(host_pname, NIS_MAXNAMELEN) 164 < 0) { 165 (void) fprintf(stderr, 166 "%s: could not get the hostname for %s\n", 167 program_name, username); 168 usage(); 169 } 170 target_host = host_pname; 171 } 172 } 173 if (__getnetnamebyuid(name, uid) == 0) { 174 (void) fprintf(stderr, 175 "%s: could not get the equivalent netname for %s\n", 176 program_name, username); 177 usage(); 178 } 179 if (pk_database == PK_NISPLUS) 180 nprinc = get_nisplus_principal(nis_local_directory(), 181 uid); 182 } else { 183 /* -h hostname option */ 184 service.h_host = target_host; 185 service.h_serv = NULL; 186 validhost = FALSE; 187 /* verify if this is a valid hostname */ 188 nc_handle = setnetconfig(); 189 if (nc_handle == NULL) { 190 /* fails to open netconfig file */ 191 (void) fprintf(stderr, 192 "%s: failed in routine setnetconfig()\n", 193 program_name); 194 exit(2); 195 } 196 while (nconf = getnetconfig(nc_handle)) { 197 /* check to see if hostname exists for this transport */ 198 if ((netdir_getbyname(nconf, &service, &addrs) == 0) && 199 (addrs->n_cnt != 0)) { 200 /* at least one valid address */ 201 validhost = TRUE; 202 break; 203 } 204 } 205 endnetconfig(nc_handle); 206 if (!validhost) { 207 (void) fprintf(stderr, "%s: unknown host: %s\n", 208 program_name, target_host); 209 exit(1); 210 } 211 (void) host2netname(name, target_host, (char *)NULL); 212 if (pk_database == PK_NISPLUS) { 213 if (target_host[strlen(target_host) - 1] != '.') { 214 sprintf(host_pname, "%s.%s", 215 target_host, nis_local_directory()); 216 nprinc = host_pname; 217 } else 218 nprinc = target_host; 219 } 220 uid = 0; 221 } 222 223 (void) fprintf(stdout, "Adding new key for %s.\n", name); 224 pass = get_password(uid, target_host, username); 225 226 if (pass == NULL) 227 exit(1); 228 229 (void) __gen_dhkeys(public, secret, pass); 230 231 memcpy(crypt1, secret, HEXKEYBYTES); 232 memcpy(crypt1 + HEXKEYBYTES, secret, KEYCHECKSUMSIZE); 233 crypt1[HEXKEYBYTES + KEYCHECKSUMSIZE] = 0; 234 xencrypt(crypt1, pass); 235 236 if (status = setpublicmap(name, public, crypt1, pk_database, 237 nprinc, 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 exit(0); 263 /* NOTREACHED */ 264 } 265 266 /* 267 * Set the entry in the public key file 268 */ 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 usage() 326 { 327 (void) fprintf(stderr, 328 "usage:\t%s -u username [-s ldap | nisplus | nis | files]\n", 329 program_name); 330 (void) fprintf(stderr, 331 "\t%s -h hostname [-s ldap | nisplus | nis | files]\n", 332 program_name); 333 exit(1); 334 } 335 336 /* 337 * The parameters passed into the routine get_password and the 338 * return values are as follows: 339 * If the -h flag was specified on the command line: 340 * (a) username is null 341 * (b) target_host is non-null 342 * (c) uid is 0 343 * (d) the login password of root on target_host is returned 344 * 345 * If the -u flag was specified on the command line: 346 * (a) username is non-null 347 * (b) target_host is null in all cases except when username is root; 348 * in that case target_host is set to the local host 349 * (c) uid is set to the username's uid 350 * (d) the login password of the user <username> is returned 351 */ 352 static char * 353 get_password(uid, target_host, username) 354 uid_t uid; 355 char *target_host; 356 char *username; 357 { 358 static char password[MAXPASSWD+1]; 359 char prompt[MAXPASSWD+MAXHOSTNAMELEN+64]; 360 char *encrypted_password, 361 *login_password = NULL, 362 *pass = NULL; 363 struct passwd *pw; 364 struct spwd *spw; 365 366 if ((username != 0) || 367 (target_host != 0) && (local_host(target_host))) { 368 369 /* 370 * "-u username" or "-h localhost" was specified on the 371 * command line 372 */ 373 374 pw = getpwuid(uid); 375 376 if (! pw) { 377 (void) fprintf(stderr, 378 "%s: unable to locate password record for uid %d\n", 379 program_name, uid); 380 return (0); 381 } 382 spw = getspnam(pw->pw_name); 383 if (spw) 384 login_password = spw->sp_pwdp; 385 386 if (! login_password || (strlen(login_password) == 0)) { 387 (void) fprintf(stderr, 388 "%s: unable to locate shadow password record for %s\n", 389 program_name, pw->pw_name); 390 return (0); 391 } 392 393 if (uid == 0) { 394 (void) sprintf(prompt, "Enter local root login password:"); 395 } else 396 (void) sprintf(prompt, "Enter %s's login password:", 397 pw->pw_name); 398 399 pass = getpass(prompt); 400 if (pass && strlen(pass) == 0) { 401 (void) fprintf(stderr, "%s: Invalid password.\n", 402 program_name); 403 return (0); 404 } 405 strcpy(password, pass); 406 encrypted_password = crypt(password, login_password); 407 408 /* Verify that password supplied matches login password */ 409 if (strcmp(encrypted_password, login_password) != 0) { 410 /* 411 * Give another chance for typo 412 */ 413 pass = getpass("Please retype password:"); 414 if (pass && strlen(pass) == 0) { 415 (void) fprintf(stderr, "%s: Invalid password.\n", 416 program_name); 417 return (0); 418 } 419 strcpy(password, pass); 420 encrypted_password = crypt(password, login_password); 421 if (strcmp(encrypted_password, login_password) != 0) { 422 (void) fprintf(stderr, 423 "%s: ERROR, invalid password.\n", 424 program_name); 425 return (0); 426 } 427 } 428 } else { 429 /* 430 * "-h remotehost" was specified on the command line 431 * 432 * Since we cannot verify the root password of the remote 433 * host we have to trust what the user inputs. We can, 434 * however, reduce the possibility of an error by prompting 435 * the user to enter the target host's password twice and 436 * comparing those two. We can also authenticate the 437 * user to be root by checking the real uid. 438 */ 439 440 if (getuid() != 0) { 441 (void) fprintf(stderr, "Must be superuser to run %s\n", 442 program_name); 443 return (0); 444 } 445 446 (void) sprintf(prompt, 447 "Enter %s's root login password:", 448 target_host); 449 pass = getpass(prompt); 450 if (!pass) { 451 (void) fprintf(stderr, 452 "%s: getpass failed.\n", 453 program_name); 454 return (0); 455 } 456 if (!*pass) { 457 (void) fprintf(stderr, 458 "%s: Invalid root password.\n", 459 program_name); 460 return (0); 461 } 462 strcpy(password, pass); 463 464 /* 465 * Now re-enter the password and compare it to the 466 * one just read. 467 */ 468 (void) sprintf(prompt, 469 "Please confirm %s's root login password:", 470 target_host); 471 pass = getpass(prompt); 472 if (!pass) { 473 (void) fprintf(stderr, 474 "%s: getpass failed.\n", 475 program_name); 476 return (0); 477 } 478 if (!*pass) { 479 (void) fprintf(stderr, 480 "%s: Invalid root password.\n", 481 program_name); 482 return (0); 483 } 484 if (strcmp(pass, password) != 0) { 485 (void) fprintf(stderr, 486 "%s: Password Incorrect.\n", 487 program_name); 488 return (0); 489 } 490 } 491 492 return (password); 493 } 494