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