1/* 2 * Copyright (c) 1999-2006 Apple Computer, Inc. All rights reserved. 3 * 4 * @APPLE_LICENSE_HEADER_START@ 5 * 6 * This file contains Original Code and/or Modifications of Original Code 7 * as defined in and that are subject to the Apple Public Source License 8 * Version 2.0 (the 'License'). You may not use this file except in 9 * compliance with the License. Please obtain a copy of the License at 10 * http://www.opensource.apple.com/apsl/ and read it before using this 11 * file. 12 * 13 * The Original Code and all software distributed under the License are 14 * distributed on an 'AS IS' basis, WITHOUT WARRANTY OF ANY KIND, EITHER 15 * EXPRESS OR IMPLIED, AND APPLE HEREBY DISCLAIMS ALL SUCH WARRANTIES, 16 * INCLUDING WITHOUT LIMITATION, ANY WARRANTIES OF MERCHANTABILITY, 17 * FITNESS FOR A PARTICULAR PURPOSE, QUIET ENJOYMENT OR NON-INFRINGEMENT. 18 * Please see the License for the specific language governing rights and 19 * limitations under the License. 20 * 21 * @APPLE_LICENSE_HEADER_END@ 22 */ 23/*- 24 * Copyright (c) 1990, 1993, 1994 25 * The Regents of the University of California. All rights reserved. 26 * Copyright (c) 2002 Networks Associates Technology, Inc. 27 * All rights reserved. 28 * 29 * Portions of this software were developed for the FreeBSD Project by 30 * ThinkSec AS and NAI Labs, the Security Research Division of Network 31 * Associates, Inc. under DARPA/SPAWAR contract N66001-01-C-8035 32 * ("CBOSS"), as part of the DARPA CHATS research program. 33 * 34 * Redistribution and use in source and binary forms, with or without 35 * modification, are permitted provided that the following conditions 36 * are met: 37 * 1. Redistributions of source code must retain the above copyright 38 * notice, this list of conditions and the following disclaimer. 39 * 2. Redistributions in binary form must reproduce the above copyright 40 * notice, this list of conditions and the following disclaimer in the 41 * documentation and/or other materials provided with the distribution. 42 * 3. All advertising materials mentioning features or use of this software 43 * must display the following acknowledgement: 44 * This product includes software developed by the University of 45 * California, Berkeley and its contributors. 46 * 4. Neither the name of the University nor the names of its contributors 47 * may be used to endorse or promote products derived from this software 48 * without specific prior written permission. 49 * 50 * THIS SOFTWARE IS PROVIDED BY THE REGENTS AND CONTRIBUTORS ``AS IS'' AND 51 * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE 52 * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE 53 * ARE DISCLAIMED. IN NO EVENT SHALL THE REGENTS OR CONTRIBUTORS BE LIABLE 54 * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL 55 * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS 56 * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) 57 * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT 58 * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY 59 * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF 60 * SUCH DAMAGE. 61 */ 62 63#if 0 64#if 0 65#ifndef lint 66static char sccsid[] = "@(#)edit.c 8.3 (Berkeley) 4/2/94"; 67#endif /* not lint */ 68#endif 69 70#include <sys/cdefs.h> 71__FBSDID("$FreeBSD: src/usr.bin/chpass/edit.c,v 1.23 2003/04/09 18:18:42 des Exp $"); 72#endif 73 74#include <sys/param.h> 75#include <sys/stat.h> 76 77#include <ctype.h> 78#include <err.h> 79#include <errno.h> 80#include <paths.h> 81#include <pwd.h> 82#include <stdio.h> 83#include <stdlib.h> 84#include <string.h> 85#include <unistd.h> 86 87#ifndef OPEN_DIRECTORY 88#include <pw_scan.h> 89#include <libutil.h> 90#endif 91 92#include "chpass.h" 93 94#ifdef OPEN_DIRECTORY 95static int display(const char *tfn, CFDictionaryRef attrs); 96static CFDictionaryRef verify(const char *tfn, CFDictionaryRef attrs); 97#else 98static int display(const char *tfn, struct passwd *pw); 99static struct passwd *verify(const char *tfn, struct passwd *pw); 100#endif 101 102#ifdef OPEN_DIRECTORY 103CFDictionaryRef 104edit(const char *tfn, CFDictionaryRef pw) 105#else 106struct passwd * 107edit(const char *tfn, struct passwd *pw) 108#endif 109{ 110#ifdef OPEN_DIRECTORY 111 CFDictionaryRef npw; 112#else 113 struct passwd *npw; 114#endif 115 char *line; 116 size_t len; 117 118 if (display(tfn, pw) == -1) 119 return (NULL); 120 for (;;) { 121#ifdef OPEN_DIRECTORY 122 switch (editfile(tfn)) { 123#else 124 switch (pw_edit(1)) { 125#endif 126 case -1: 127 return (NULL); 128 case 0: 129#ifdef OPEN_DIRECTORY 130 return (NULL); 131#else 132 return (pw_dup(pw)); 133#endif 134 default: 135 break; 136 } 137 if ((npw = verify(tfn, pw)) != NULL) 138 return (npw); 139#ifndef OPEN_DIRECTORY 140 free(npw); 141#endif 142 printf("re-edit the password file? "); 143 fflush(stdout); 144 if ((line = fgetln(stdin, &len)) == NULL) { 145 warn("fgetln()"); 146 return (NULL); 147 } 148 if (len > 0 && (*line == 'N' || *line == 'n')) 149 return (NULL); 150 } 151} 152 153/* 154 * display -- 155 * print out the file for the user to edit; strange side-effect: 156 * set conditional flag if the user gets to edit the shell. 157 */ 158#if OPEN_DIRECTORY 159static int 160display(const char *tfn, CFDictionaryRef attrs) 161#else 162static int 163display(const char *tfn, struct passwd *pw) 164#endif 165{ 166 FILE *fp; 167#ifndef OPEN_DIRECTORY 168 char *bp, *gecos, *p; 169#endif 170 171 if ((fp = fopen(tfn, "w")) == NULL) { 172 warn("%s", tfn); 173 return (-1); 174 } 175 176#ifdef OPEN_DIRECTORY 177 CFArrayRef values = CFDictionaryGetValue(attrs, kODAttributeTypeRecordName); 178 CFStringRef username = (values && CFArrayGetCount(values)) > 0 ? CFArrayGetValueAtIndex(values, 0) : NULL; 179 180 (void)cfprintf(fp, 181 "# Changing user information for %@.\n" 182 "# Use \"passwd\" to change the password.\n" 183 "##\n" 184 "# Open Directory%s%@\n" 185 "##\n", 186 username, 187 DSPath ? ": " : "", 188 DSPath ? DSPath : CFSTR("")); 189 190 int ndisplayed = 0; 191 ENTRY* ep; 192 for (ep = list; ep->prompt; ep++) 193 if (!ep->restricted) { 194 ep->display(attrs, *ep->attrName, ep->prompt, fp); 195 ndisplayed++; 196 } 197 if(!ndisplayed) { 198 (void)fprintf(fp, "###################################\n"); 199 (void)fprintf(fp, "# No fields are available to change\n"); 200 (void)fprintf(fp, "###################################\n"); 201 } 202#else OPEN_DIRECTORY 203 (void)fprintf(fp, 204 "#Changing user information for %s.\n", pw->pw_name); 205 if (master_mode) { 206 (void)fprintf(fp, "Login: %s\n", pw->pw_name); 207 (void)fprintf(fp, "Password: %s\n", pw->pw_passwd); 208 (void)fprintf(fp, "Uid [#]: %lu\n", (unsigned long)pw->pw_uid); 209 (void)fprintf(fp, "Gid [# or name]: %lu\n", 210 (unsigned long)pw->pw_gid); 211 (void)fprintf(fp, "Change [month day year]: %s\n", 212 ttoa(pw->pw_change)); 213 (void)fprintf(fp, "Expire [month day year]: %s\n", 214 ttoa(pw->pw_expire)); 215 (void)fprintf(fp, "Class: %s\n", pw->pw_class); 216 (void)fprintf(fp, "Home directory: %s\n", pw->pw_dir); 217 (void)fprintf(fp, "Shell: %s\n", 218 *pw->pw_shell ? pw->pw_shell : _PATH_BSHELL); 219 } 220 /* Only admin can change "restricted" shells. */ 221#if 0 222 else if (ok_shell(pw->pw_shell)) 223 /* 224 * Make shell a restricted field. Ugly with a 225 * necklace, but there's not much else to do. 226 */ 227#else 228 else if ((!list[E_SHELL].restricted && ok_shell(pw->pw_shell)) || 229 master_mode) 230 /* 231 * If change not restrict (table.c) and standard shell 232 * OR if root, then allow editing of shell. 233 */ 234#endif 235 (void)fprintf(fp, "Shell: %s\n", 236 *pw->pw_shell ? pw->pw_shell : _PATH_BSHELL); 237 else 238 list[E_SHELL].restricted = 1; 239 240 if ((bp = gecos = strdup(pw->pw_gecos)) == NULL) { 241 warn(NULL); 242 fclose(fp); 243 return (-1); 244 } 245 246 p = strsep(&bp, ","); 247 p = strdup(p ? p : ""); 248 list[E_NAME].save = p; 249 if (!list[E_NAME].restricted || master_mode) 250 (void)fprintf(fp, "Full Name: %s\n", p); 251 252 p = strsep(&bp, ","); 253 p = strdup(p ? p : ""); 254 list[E_LOCATE].save = p; 255 if (!list[E_LOCATE].restricted || master_mode) 256 (void)fprintf(fp, "Office Location: %s\n", p); 257 258 p = strsep(&bp, ","); 259 p = strdup(p ? p : ""); 260 list[E_BPHONE].save = p; 261 if (!list[E_BPHONE].restricted || master_mode) 262 (void)fprintf(fp, "Office Phone: %s\n", p); 263 264 p = strsep(&bp, ","); 265 p = strdup(p ? p : ""); 266 list[E_HPHONE].save = p; 267 if (!list[E_HPHONE].restricted || master_mode) 268 (void)fprintf(fp, "Home Phone: %s\n", p); 269 270 bp = strdup(bp ? bp : ""); 271 list[E_OTHER].save = bp; 272 if (!list[E_OTHER].restricted || master_mode) 273 (void)fprintf(fp, "Other information: %s\n", bp); 274 275 free(gecos); 276#endif /* OPEN_DIRECTORY */ 277 278 (void)fchown(fileno(fp), getuid(), getgid()); 279 (void)fclose(fp); 280 return (0); 281} 282 283#ifdef OPEN_DIRECTORY 284static CFDictionaryRef 285verify(const char* tfn, CFDictionaryRef pw) 286#else 287static struct passwd * 288verify(const char *tfn, struct passwd *pw) 289#endif 290{ 291#ifdef OPEN_DIRECTORY 292 CFMutableDictionaryRef npw; 293#else 294 struct passwd *npw; 295#endif 296 ENTRY *ep; 297 char *buf, *p, *val; 298 struct stat sb; 299 FILE *fp; 300 int line; 301 size_t len; 302 303#ifdef OPEN_DIRECTORY 304 if ((npw = CFDictionaryCreateMutable(NULL, 0, &kCFTypeDictionaryKeyCallBacks, &kCFTypeDictionaryValueCallBacks)) == NULL) 305 return (NULL); 306#else 307 if ((pw = pw_dup(pw)) == NULL) 308 return (NULL); 309#endif 310 if ((fp = fopen(tfn, "r")) == NULL || 311 fstat(fileno(fp), &sb) == -1) { 312 warn("%s", tfn); 313#ifndef OPEN_DIRECTORY 314 free(pw); 315#endif 316 return (NULL); 317 } 318 if (sb.st_size == 0) { 319 warnx("corrupted temporary file"); 320 fclose(fp); 321#ifndef OPEN_DIRECTORY 322 free(pw); 323#endif 324 return (NULL); 325 } 326 val = NULL; 327 for (line = 1; (buf = fgetln(fp, &len)) != NULL; ++line) { 328 if (*buf == '\0' || *buf == '#') 329 continue; 330 while (len > 0 && isspace(buf[len - 1])) 331 --len; 332 for (ep = list;; ++ep) { 333 if (!ep->prompt) { 334 warnx("%s: unrecognized field on line %d", 335 tfn, line); 336 goto bad; 337 } 338 if (ep->len > len) 339 continue; 340 if (strncasecmp(buf, ep->prompt, ep->len) != 0) 341 continue; 342 if (ep->restricted && !master_mode) { 343 warnx("%s: you may not change the %s field", 344 tfn, ep->prompt); 345 goto bad; 346 } 347 for (p = buf; p < buf + len && *p != ':'; ++p) 348 /* nothing */ ; 349 if (*p != ':') { 350 warnx("%s: line %d corrupted", tfn, line); 351 goto bad; 352 } 353 while (++p < buf + len && isspace(*p)) 354 /* nothing */ ; 355 free(val); 356 asprintf(&val, "%.*s", (int)(buf + len - p), p); 357 if (val == NULL) 358 goto bad; 359 if (ep->except && strpbrk(val, ep->except)) { 360 warnx("%s: invalid character in \"%s\" field '%s'", 361 tfn, ep->prompt, val); 362 goto bad; 363 } 364#ifdef OPEN_DIRECTORY 365 if ((ep->func)(val, NULL, NULL)) 366 goto bad; 367 { 368 CFStringRef str = CFStringCreateWithCString(NULL, val, kCFStringEncodingUTF8); 369 if (str) { 370 CFDictionarySetValue(npw, *ep->attrName, str); 371 CFRelease(str); 372 } 373 } 374#else 375 if ((ep->func)(val, pw, ep)) 376 goto bad; 377#endif 378 break; 379 } 380 } 381 free(val); 382 fclose(fp); 383 384#ifndef OPEN_DIRECTORY 385 /* Build the gecos field. */ 386 len = asprintf(&p, "%s,%s,%s,%s,%s", list[E_NAME].save, 387 list[E_LOCATE].save, list[E_BPHONE].save, 388 list[E_HPHONE].save, list[E_OTHER].save); 389 if (p == NULL) { 390 warn("asprintf()"); 391 free(pw); 392 return (NULL); 393 } 394 while (len > 0 && p[len - 1] == ',') 395 p[--len] = '\0'; 396 pw->pw_gecos = p; 397 buf = pw_make(pw); 398 free(pw); 399 free(p); 400 if (buf == NULL) { 401 warn("pw_make()"); 402 return (NULL); 403 } 404 npw = pw_scan(buf, PWSCAN_WARN|PWSCAN_MASTER); 405#endif /* !OPEN_DIRECTORY */ 406 free(buf); 407 return (npw); 408bad: 409#ifndef OPEN_DIRECTORY 410 free(pw); 411#endif 412 free(val); 413 fclose(fp); 414 return (NULL); 415} 416