diff options
author | Cameron Katri <me@cameronkatri.com> | 2021-05-09 14:20:58 -0400 |
---|---|---|
committer | Cameron Katri <me@cameronkatri.com> | 2021-05-09 14:20:58 -0400 |
commit | 5fd83771641d15c418f747bd343ba6738d3875f7 (patch) | |
tree | 5abf0f78f680d9837dbd93d4d4c3933bb7509599 /system_cmds/passwd.tproj | |
download | apple_cmds-5fd83771641d15c418f747bd343ba6738d3875f7.tar.gz apple_cmds-5fd83771641d15c418f747bd343ba6738d3875f7.tar.zst apple_cmds-5fd83771641d15c418f747bd343ba6738d3875f7.zip |
Import macOS userland
adv_cmds-176
basic_cmds-55
bootstrap_cmds-116.100.1
developer_cmds-66
diskdev_cmds-667.40.1
doc_cmds-53.60.1
file_cmds-321.40.3
mail_cmds-35
misc_cmds-34
network_cmds-606.40.1
patch_cmds-17
remote_cmds-63
shell_cmds-216.60.1
system_cmds-880.60.2
text_cmds-106
Diffstat (limited to 'system_cmds/passwd.tproj')
-rw-r--r-- | system_cmds/passwd.tproj/file_passwd.c | 237 | ||||
-rw-r--r-- | system_cmds/passwd.tproj/nis_passwd.c | 265 | ||||
-rw-r--r-- | system_cmds/passwd.tproj/od_passwd.c | 253 | ||||
-rw-r--r-- | system_cmds/passwd.tproj/pam_passwd.c | 80 | ||||
-rw-r--r-- | system_cmds/passwd.tproj/passwd.1 | 134 | ||||
-rw-r--r-- | system_cmds/passwd.tproj/passwd.c | 302 | ||||
-rw-r--r-- | system_cmds/passwd.tproj/passwd.entitlements | 12 | ||||
-rw-r--r-- | system_cmds/passwd.tproj/passwd.h | 47 | ||||
-rw-r--r-- | system_cmds/passwd.tproj/passwd.pam | 5 |
9 files changed, 1335 insertions, 0 deletions
diff --git a/system_cmds/passwd.tproj/file_passwd.c b/system_cmds/passwd.tproj/file_passwd.c new file mode 100644 index 0000000..62d27f3 --- /dev/null +++ b/system_cmds/passwd.tproj/file_passwd.c @@ -0,0 +1,237 @@ +/* + * Copyright (c) 1999-2016 Apple Inc. All rights reserved. + * + * @APPLE_LICENSE_HEADER_START@ + * + * This file contains Original Code and/or Modifications of Original Code + * as defined in and that are subject to the Apple Public Source License + * Version 2.0 (the 'License'). You may not use this file except in + * compliance with the License. Please obtain a copy of the License at + * http://www.opensource.apple.com/apsl/ and read it before using this + * file. + * + * The Original Code and all software distributed under the License are + * distributed on an 'AS IS' basis, WITHOUT WARRANTY OF ANY KIND, EITHER + * EXPRESS OR IMPLIED, AND APPLE HEREBY DISCLAIMS ALL SUCH WARRANTIES, + * INCLUDING WITHOUT LIMITATION, ANY WARRANTIES OF MERCHANTABILITY, + * FITNESS FOR A PARTICULAR PURPOSE, QUIET ENJOYMENT OR NON-INFRINGEMENT. + * Please see the License for the specific language governing rights and + * limitations under the License. + * + * @APPLE_LICENSE_HEADER_END@ + */ +#include <ctype.h> +#include <err.h> +#include <errno.h> +#include <fcntl.h> +#include <pwd.h> +#include <signal.h> +#include <stdbool.h> +#include <stdlib.h> +#include <stdio.h> +#include <string.h> +#include <sysexits.h> +#include <unistd.h> +#include <sys/time.h> +#include <sys/stat.h> +#include "passwd.h" + +#define _PASSWD_FILE "/etc/master.passwd" +#define _COMPAT_FILE "/etc/passwd" +#define _PASSWD_FIELDS 10 +#define BUFSIZE 8192 + +void getpasswd(char *, int, int, int, int, char *, char **, char**, char **); + +static struct passwd * +parse_user(char *line, size_t len) +{ + static struct passwd pw; + int i,j; + char *tokens[_PASSWD_FIELDS]; + char *token = NULL; + bool comment = true; + + free(pw.pw_name); + free(pw.pw_passwd); + free(pw.pw_class); + free(pw.pw_gecos); + free(pw.pw_dir); + free(pw.pw_shell); + memset(&pw, 0, sizeof(pw)); + + if (line == NULL) return NULL; + + memset(&tokens, 0, sizeof(char *) * _PASSWD_FIELDS); + + for (i = 0, j = 0; i < len && j < _PASSWD_FIELDS; ++i) { + int c = line[i]; + if (!isspace(c) && c != '#') { + comment = false; + } + if (!comment && token == NULL) { + // start a new token + token = &line[i]; + } else if (token && (c == ':' || c == '\n')) { + // end the current token + // special case for empty token + while (token[0] == ':' && token < &line[i]) { + tokens[j++] = strdup(""); + ++token; + } + tokens[j++] = strndup(token, &line[i] - token); + token = NULL; + } + } + + if (comment || j != _PASSWD_FIELDS) return NULL; + + j = 0; + pw.pw_name = tokens[j++]; + pw.pw_passwd = tokens[j++]; + pw.pw_uid = atoi(tokens[j]); + free(tokens[j++]); + pw.pw_gid = atoi(tokens[j]); + free(tokens[j++]); + pw.pw_class = tokens[j++]; + pw.pw_change = atoi(tokens[j]); + free(tokens[j++]); + pw.pw_expire = atoi(tokens[j]); + free(tokens[j++]); + pw.pw_gecos = tokens[j++]; + pw.pw_dir = tokens[j++]; + pw.pw_shell = tokens[j++]; + + return &pw; +} + +static struct passwd * +find_user(FILE *fp, char *uname) +{ + size_t len; + char *line; + + rewind(fp); + + while ((line = fgetln(fp, &len)) != NULL) { + struct passwd *pw = parse_user(line, len); + if (pw && strcmp(uname, pw->pw_name) == 0) { + return pw; + } + } + return NULL; +} + +static void +rewrite_file(char *path, FILE *fp, struct passwd *newpw) +{ + int fd; + char *line; + size_t len; + FILE *tfp = NULL; + char *tempname = NULL; // temporary master.passwd file + + asprintf(&tempname, "%s.XXXXXX", path); + + fd = mkstemp(tempname); + if (fd == -1) { + err(EXIT_FAILURE, "%s", tempname); + } + tfp = fdopen(fd, "w+"); + if (tfp == NULL || fchmod(fd, S_IRUSR | S_IWUSR) != 0) { + int save = errno; + unlink(tempname); + errno = save; + err(EXIT_FAILURE, "%s", tempname); + } + + while ((line = fgetln(fp, &len)) != NULL) { + struct passwd *pw = parse_user(line, len); + + // if this is not the entry we're looking for or if parsing + // failed (likely a comment) then print the entry as is. + if (pw == NULL || strcmp(newpw->pw_name, pw->pw_name) != 0) { + fwrite(line, sizeof(char), len, tfp); + } else { + fprintf(tfp, "%s:%s:%d:%d:%s:%ld:%ld:%s:%s:%s\n", + newpw->pw_name, + newpw->pw_passwd, + newpw->pw_uid, + newpw->pw_gid, + newpw->pw_class, + newpw->pw_change, + newpw->pw_expire, + newpw->pw_gecos, + newpw->pw_dir, + newpw->pw_shell); + } + } + + // Move the temporary file into place. + if (fclose(tfp) != 0 || rename(tempname, path) != 0) { + int save = errno; + unlink(tempname); + errno = save; + err(EXIT_FAILURE, "%s", tempname); + } + + free(tempname); +} + +int +file_passwd(char *uname, char *locn) +{ + char *ne, *oc, *nc; + int fd; + FILE *fp; + uid_t uid; + char *fname; + struct passwd *pw; + struct passwd newpw; + + fname = _PASSWD_FILE; + if (locn != NULL) fname = locn; + + fd = open(fname, O_RDONLY | O_EXLOCK); + if (fd == -1) { + err(EXIT_FAILURE, "%s", fname); + } + + fp = fdopen(fd, "r"); + if (fp == NULL) { + err(EXIT_FAILURE, "%s", fname); + } + + pw = find_user(fp, uname); + if (pw == NULL) { + errx(EXIT_FAILURE, "user %s not found in %s", uname, fname); + } + + uid = getuid(); + if (uid != 0 && uid != pw->pw_uid) { + errno = EACCES; + err(EXIT_FAILURE, "%s", uname); + } + + // Get the password + getpasswd(uname, (uid == 0), 5, 0, 0, pw->pw_passwd, &ne, &oc, &nc); + + newpw.pw_name = strdup(pw->pw_name); + newpw.pw_passwd = strdup(ne); + newpw.pw_uid = pw->pw_uid; + newpw.pw_gid = pw->pw_gid; + newpw.pw_class = strdup(pw->pw_class); + newpw.pw_change = pw->pw_change; + newpw.pw_expire = pw->pw_expire; + newpw.pw_gecos = strdup(pw->pw_gecos); + newpw.pw_dir = strdup(pw->pw_dir); + newpw.pw_shell = strdup(pw->pw_shell); + + // Rewrite the file + rewind(fp); + rewrite_file(fname, fp, &newpw); + + fclose(fp); + + return 0; +} diff --git a/system_cmds/passwd.tproj/nis_passwd.c b/system_cmds/passwd.tproj/nis_passwd.c new file mode 100644 index 0000000..1525096 --- /dev/null +++ b/system_cmds/passwd.tproj/nis_passwd.c @@ -0,0 +1,265 @@ +/* + * Copyright (c) 1999-2016 Apple Inc. All rights reserved. + * + * @APPLE_LICENSE_HEADER_START@ + * + * "Portions Copyright (c) 1999 Apple Computer, Inc. All Rights + * Reserved. This file contains Original Code and/or Modifications of + * Original Code as defined in and that are subject to the Apple Public + * Source License Version 1.0 (the 'License'). You may not use this file + * except in compliance with the License. Please obtain a copy of the + * License at http://www.apple.com/publicsource and read it before using + * this file. + * + * The Original Code and all software distributed under the License are + * distributed on an 'AS IS' basis, WITHOUT WARRANTY OF ANY KIND, EITHER + * EXPRESS OR IMPLIED, AND APPLE HEREBY DISCLAIMS ALL SUCH WARRANTIES, + * INCLUDING WITHOUT LIMITATION, ANY WARRANTIES OF MERCHANTABILITY, + * FITNESS FOR A PARTICULAR PURPOSE OR NON-INFRINGEMENT. Please see the + * License for the specific language governing rights and limitations + * under the License." + * + * @APPLE_LICENSE_HEADER_END@ + */ +/* + * Portions Copyright (c) 1998 by Apple Computer, Inc. + * Portions Copyright (c) 1988 by Sun Microsystems, Inc. + * Portions Copyright (c) 1988 The Regents of the University of California. + * All rights reserved. + * + * Redistribution and use in source and binary forms, with or without + * modification, are permitted provided that the following conditions + * are met: + * 1. Redistributions of source code must retain the above copyright + * notice, this list of conditions and the following disclaimer. + * 2. Redistributions in binary form must reproduce the above copyright + * notice, this list of conditions and the following disclaimer in the + * documentation and/or other materials provided with the distribution. + * 3. All advertising materials mentioning features or use of this software + * must display the following acknowledgement: + * This product includes software developed by the University of + * California, Berkeley and its contributors. + * 4. Neither the name of the University nor the names of its contributors + * may be used to endorse or promote products derived from this software + * without specific prior written permission. + * + * THIS SOFTWARE IS PROVIDED BY THE REGENTS AND CONTRIBUTORS ``AS IS'' AND + * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE + * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE + * ARE DISCLAIMED. IN NO EVENT SHALL THE REGENTS OR CONTRIBUTORS BE LIABLE + * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL + * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS + * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) + * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT + * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY + * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF + * SUCH DAMAGE. + */ + +#include "passwd.h" + +#ifdef INFO_NIS + +/* update a user's password in NIS. This was based on the Sun implementation + * we used in NEXTSTEP, although I've added some stuff from OpenBSD. And + * it uses the API to support Rhapsody's proprietry infosystem switch. + * lukeh + */ + +#include <stdio.h> +#include <stdlib.h> +#include <unistd.h> +#include <string.h> +#include <pwd.h> +#include <netinet/in.h> +#include <rpc/types.h> +#include <rpc/xdr.h> +#include <rpc/rpc.h> +#include <rpcsvc/yp_prot.h> +#include <rpcsvc/ypclnt.h> +#include <rpcsvc/yppasswd.h> +#include <netdb.h> +#include <sys/socket.h> +#include <sys/file.h> +#include <errno.h> + +extern int getrpcport(char *, int, int, int); +extern void getpasswd(char *, int, int, int, int, char *, char **, char**, char **); + +static struct passwd *ypgetpwnam(char *name, char *domain); +static struct passwd *interpret(struct passwd *pwent, char *line); + +int nis_passwd(char *uname, char *domain) +{ + int ans, port, ok = -1; + char *master; + char *ne; /* new encrypted password */ + char *oc; /* old cleartext password */ + char *nc; /* new cleartext password */ + static struct yppasswd yppasswd; + struct passwd *pwd; + int uid; + struct timeval tv; + CLIENT *cl; + + if (domain == NULL) + { + if (yp_get_default_domain(&domain) != 0) + { + (void)fprintf(stderr, "can't get domain\n"); + exit(1); + } + } + + if (yp_master(domain, "passwd.byname", &master) != 0) + { + (void)fprintf(stderr, "can't get master for passwd file\n"); + exit(1); + } + + port = getrpcport(master, YPPASSWDPROG, YPPASSWDPROC_UPDATE, + IPPROTO_UDP); + if (port == 0) + { + (void)fprintf(stderr, "%s is not running yppasswd daemon\n", + master); + exit(1); + } + if (port >= IPPORT_RESERVED) + { + (void)fprintf(stderr, + "yppasswd daemon is not running on privileged port\n"); + exit(1); + } + + pwd = ypgetpwnam(uname, domain); + if (pwd == NULL) + { + (void)fprintf(stderr, "user %s not found\n", uname); + exit(1); + } + + uid = getuid(); + if (uid != 0 && uid != pwd->pw_uid) + { + (void)fprintf(stderr, "you may only change your own password\n"); + exit(1); + } + + getpasswd(uname, 0, 5, 0, 0, pwd->pw_passwd, &ne, &oc, &nc); + + yppasswd.oldpass = oc; + yppasswd.newpw.pw_name = pwd->pw_name; + yppasswd.newpw.pw_passwd = ne; + yppasswd.newpw.pw_uid = pwd->pw_uid; + yppasswd.newpw.pw_gid = pwd->pw_gid; + yppasswd.newpw.pw_gecos = pwd->pw_gecos; + yppasswd.newpw.pw_dir = pwd->pw_dir; + yppasswd.newpw.pw_shell = pwd->pw_shell; + + cl = clnt_create(master, YPPASSWDPROG, YPPASSWDVERS, "udp"); + if (cl == NULL) + { + (void)fprintf(stderr, "could not contact yppasswdd on %s\n", master); + exit(1); + } + cl->cl_auth = authunix_create_default(); + tv.tv_sec = 2; + tv.tv_usec = 0; + ans = clnt_call(cl, YPPASSWDPROC_UPDATE, + (xdrproc_t)xdr_yppasswd, &yppasswd, (xdrproc_t)xdr_int, &ok, tv); + + if (ans != 0) + { + clnt_perrno(ans); + (void)fprintf(stderr, "\n"); + (void)fprintf(stderr, "couldn't change passwd\n"); + exit(1); + } + if (ok != 0) + { + (void)fprintf(stderr, "couldn't change passwd\n"); + exit(1); + } + return(0); +} + +static char * +pwskip(register char *p) +{ + while (*p && *p != ':' && *p != '\n') + ++p; + if (*p) + *p++ = 0; + return (p); +} + +static struct passwd * +interpret(struct passwd *pwent, char *line) +{ + register char *p = line; + + pwent->pw_passwd = "*"; + pwent->pw_uid = 0; + pwent->pw_gid = 0; + pwent->pw_gecos = ""; + pwent->pw_dir = ""; + pwent->pw_shell = ""; +#ifndef __SLICK__ + pwent->pw_change = 0; + pwent->pw_expire = 0; + pwent->pw_class = ""; +#endif + + /* line without colon separators is no good, so ignore it */ + if(!strchr(p, ':')) + return(NULL); + + pwent->pw_name = p; + p = pwskip(p); + pwent->pw_passwd = p; + p = pwskip(p); + pwent->pw_uid = (uid_t)strtoul(p, NULL, 10); + p = pwskip(p); + pwent->pw_gid = (gid_t)strtoul(p, NULL, 10); + p = pwskip(p); + pwent->pw_gecos = p; + p = pwskip(p); + pwent->pw_dir = p; + p = pwskip(p); + pwent->pw_shell = p; + while (*p && *p != '\n') + p++; + *p = '\0'; + return (pwent); +} + + +static struct passwd * +ypgetpwnam(char *nam, char *domain) +{ + static struct passwd pwent; + char *val; + int reason, vallen; + static char *__yplin = NULL; + + reason = yp_match(domain, "passwd.byname", nam, (int)strlen(nam), + &val, &vallen); + switch(reason) { + case 0: + break; + default: + return (NULL); + break; + } + val[vallen] = '\0'; + if (__yplin) + free(__yplin); + __yplin = (char *)malloc(vallen + 1); + strcpy(__yplin, val); + free(val); + + return(interpret(&pwent, __yplin)); +} + +#endif /* INFO_NIS */ diff --git a/system_cmds/passwd.tproj/od_passwd.c b/system_cmds/passwd.tproj/od_passwd.c new file mode 100644 index 0000000..f24883a --- /dev/null +++ b/system_cmds/passwd.tproj/od_passwd.c @@ -0,0 +1,253 @@ +/* + * Copyright (c) 1999-2016 Apple Inc. All rights reserved. + * + * @APPLE_LICENSE_HEADER_START@ + * + * This file contains Original Code and/or Modifications of Original Code + * as defined in and that are subject to the Apple Public Source License + * Version 2.0 (the 'License'). You may not use this file except in + * compliance with the License. Please obtain a copy of the License at + * http://www.opensource.apple.com/apsl/ and read it before using this + * file. + * + * The Original Code and all software distributed under the License are + * distributed on an 'AS IS' basis, WITHOUT WARRANTY OF ANY KIND, EITHER + * EXPRESS OR IMPLIED, AND APPLE HEREBY DISCLAIMS ALL SUCH WARRANTIES, + * INCLUDING WITHOUT LIMITATION, ANY WARRANTIES OF MERCHANTABILITY, + * FITNESS FOR A PARTICULAR PURPOSE, QUIET ENJOYMENT OR NON-INFRINGEMENT. + * Please see the License for the specific language governing rights and + * limitations under the License. + * + * @APPLE_LICENSE_HEADER_END@ + */ +#include <stdio.h> +#include <unistd.h> +#include <pwd.h> +#include <sys/sysctl.h> + +#include "passwd.h" + +#ifdef INFO_OPEN_DIRECTORY + +#include <CoreFoundation/CoreFoundation.h> +#include <OpenDirectory/OpenDirectory.h> +#include <OpenDirectory/OpenDirectoryPriv.h> + +extern char* progname; +int master_mode; + +static int +cfprintf(FILE* file, const char* format, ...) { + char* cstr; + int result = 0; + va_list args; + va_start(args, format); + CFStringRef formatStr = CFStringCreateWithCStringNoCopy(NULL, format, kCFStringEncodingUTF8, kCFAllocatorNull); + if (formatStr) { + CFStringRef str = CFStringCreateWithFormatAndArguments(NULL, NULL, formatStr, args); + if (str) { + size_t size = CFStringGetMaximumSizeForEncoding(CFStringGetLength(str), kCFStringEncodingUTF8) + 1; + va_end(args); + cstr = malloc(size); + if (cstr && CFStringGetCString(str, cstr, size, kCFStringEncodingUTF8)) { + result = fprintf(file, "%s", cstr); + free(cstr); + } + CFRelease(str); + } + CFRelease(formatStr); + } + return result; +} + +static void +show_error(CFErrorRef error) { + if (error) { + CFStringRef desc = CFErrorCopyDescription(error); + if (desc) { + cfprintf(stderr, "%s: %@", progname, desc); + CFRelease(desc); + } + desc = CFErrorCopyFailureReason(error); + if (desc) cfprintf(stderr, " %@", desc); + + desc = CFErrorCopyRecoverySuggestion(error); + if (desc) cfprintf(stderr, " %@", desc); + + fprintf(stderr, "\n"); + } +} + +int +od_passwd(char* uname, char* locn, char* aname) +{ + int change_pass_on_self; + CFErrorRef error = NULL; + CFStringRef username = NULL; + CFStringRef location = NULL; + CFStringRef authname = NULL; + ODNodeRef node = NULL; + ODRecordRef rec = NULL; + CFStringRef oldpass = NULL; + CFStringRef newpass = NULL; + + if (uname == NULL) + return -1; + + /* + * If no explicit authorization name was specified (via -u) + * then default to the target user. + */ + if (!aname) { + aname = strdup(uname); + } + + master_mode = (geteuid() == 0); + change_pass_on_self = (strcmp(aname, uname) == 0); + + if (locn) { + location = CFStringCreateWithCString(NULL, locn, kCFStringEncodingUTF8); + } + + if (aname) { + authname = CFStringCreateWithCString(NULL, aname, kCFStringEncodingUTF8); + } + + if (uname) { + username = CFStringCreateWithCString(NULL, uname, kCFStringEncodingUTF8); + if (!username) return -1; + } + + /* + * Copy the record from the specified node, or perform a search. + */ + if (location) { + node = ODNodeCreateWithName(NULL, kODSessionDefault, location, &error); + } else { + node = ODNodeCreateWithNodeType(NULL, kODSessionDefault, kODNodeTypeAuthentication, &error); + } + + if (node) { + rec = ODNodeCopyRecord(node, kODRecordTypeUsers, username, NULL, &error ); + CFRelease(node); + } + + if (!rec) { + if (error) { + show_error(error); + } else { + fprintf(stderr, "%s: Unknown user name '%s'.\n", progname, uname); + } + return -1; + } + + /* + * Get the actual location. + */ + CFArrayRef values = NULL; + values = ODRecordCopyValues(rec, kODAttributeTypeMetaNodeLocation, &error); + location = (values && CFArrayGetCount(values) > 0) ? CFArrayGetValueAtIndex(values, 0) : location; + + printf("Changing password for %s.\n", uname); + + bool isSecureToken = false; + if (master_mode) { + CFArrayRef authorityValues = NULL; + authorityValues = ODRecordCopyValues(rec, kODAttributeTypeAuthenticationAuthority, &error); + if (authorityValues != NULL) { + isSecureToken = CFArrayContainsValue(authorityValues, CFRangeMake(0, CFArrayGetCount(authorityValues)), (const void *)CFSTR(";SecureToken;")); + CFRelease(authorityValues); + } + } + + /* + * Prompt for password if not super-user, or if changing a remote node, or if changing a record that uses SecureToken. + */ + int needs_auth = (!master_mode || CFStringCompareWithOptions(location, CFSTR("/Local/"), CFRangeMake(0, 7), 0) != kCFCompareEqualTo || isSecureToken); + + if (needs_auth) { + char prompt[BUFSIZ]; + if (change_pass_on_self) { + strlcpy(prompt, "Old password:", sizeof(prompt)); + } else { + snprintf(prompt, sizeof(prompt), "Password for %s:", aname); + } + char *p = getpass( prompt ); + if (p) { + oldpass = CFStringCreateWithCString(NULL, p, kCFStringEncodingUTF8); + memset(p, 0, strlen(p)); + + if (!change_pass_on_self) { + if (!ODRecordSetNodeCredentials(rec, authname, oldpass, &error)) { + show_error(error); + exit(1); + } + CFRelease(oldpass); + oldpass = NULL; + } + } + } + + for (;;) { + char *p = getpass("New password:"); + if (p && strlen(p) > 0) { + newpass = CFStringCreateWithCString(NULL, p, kCFStringEncodingUTF8); + memset(p, 0, strlen(p)); + } else { + printf("Password unchanged.\n"); + exit(0); + } + + p = getpass("Retype new password:"); + if (p) { + CFStringRef verify = CFStringCreateWithCString(NULL, p, kCFStringEncodingUTF8); + if (!verify || !CFEqual(newpass, verify)) { + if (verify) CFRelease(verify); + printf("Mismatch; try again, EOF to quit.\n"); + } else { + CFRelease(verify); + break; + } + } + } + + ODRecordChangePassword(rec, oldpass, newpass, &error); + + if (error) { + show_error(error); + exit(1); + } + + if (oldpass) CFRelease(oldpass); + if (newpass) CFRelease(newpass); + +#if 0 + if ( status != eDSNoErr ) { + switch( status ) + { + case eDSAuthPasswordTooShort: + errMsgStr = "The new password is too short."; + break; + + case eDSAuthPasswordTooLong: + errMsgStr = "The new password is too long."; + break; + + case eDSAuthPasswordNeedsLetter: + errMsgStr = "The new password must contain a letter."; + break; + + case eDSAuthPasswordNeedsDigit: + errMsgStr = "The new password must contain a number."; + break; + + default: + errMsgStr = "Sorry"; + } + fprintf(stderr, "%s\n", errMsgStr); + exit(1); +#endif + return 0; +} + +#endif /* INFO_OPEN_DIRECTORY */ diff --git a/system_cmds/passwd.tproj/pam_passwd.c b/system_cmds/passwd.tproj/pam_passwd.c new file mode 100644 index 0000000..aabf2f1 --- /dev/null +++ b/system_cmds/passwd.tproj/pam_passwd.c @@ -0,0 +1,80 @@ +/* + * Copyright (c) 1999-2016 Apple Inc. All rights reserved. + * + * @APPLE_LICENSE_HEADER_START@ + * + * This file contains Original Code and/or Modifications of Original Code + * as defined in and that are subject to the Apple Public Source License + * Version 2.0 (the 'License'). You may not use this file except in + * compliance with the License. Please obtain a copy of the License at + * http://www.opensource.apple.com/apsl/ and read it before using this + * file. + * + * The Original Code and all software distributed under the License are + * distributed on an 'AS IS' basis, WITHOUT WARRANTY OF ANY KIND, EITHER + * EXPRESS OR IMPLIED, AND APPLE HEREBY DISCLAIMS ALL SUCH WARRANTIES, + * INCLUDING WITHOUT LIMITATION, ANY WARRANTIES OF MERCHANTABILITY, + * FITNESS FOR A PARTICULAR PURPOSE, QUIET ENJOYMENT OR NON-INFRINGEMENT. + * Please see the License for the specific language governing rights and + * limitations under the License. + * + * @APPLE_LICENSE_HEADER_END@ + */ +#include <stdio.h> +#include "passwd.h" + +#ifdef INFO_PAM + +#include <security/pam_appl.h> +#include <security/openpam.h> /* for openpam_ttyconv() */ + +extern char* progname; +static pam_handle_t *pamh; +static struct pam_conv pamc; + +int +pam_passwd(char* uname) +{ + int retval = PAM_SUCCESS; + + /* Initialize PAM. */ + pamc.conv = &openpam_ttyconv; + pam_start(progname, uname, &pamc, &pamh); + + /* Authenticate. */ + if (PAM_SUCCESS != (retval = pam_authenticate(pamh, 0))) + goto pamerr; + + /* Authorize. */ + if (PAM_SUCCESS != (retval = pam_acct_mgmt(pamh, 0)) && PAM_NEW_AUTHTOK_REQD != retval) + goto pamerr; + + printf("Changing password for %s.\n", uname); + + /* Change the password. */ + if (PAM_SUCCESS != (retval = pam_chauthtok(pamh, 0))) + goto pamerr; + + /* Set the credentials. */ + if (PAM_SUCCESS != (retval = pam_setcred(pamh, PAM_ESTABLISH_CRED))) + goto pamerr; + + /* Open the session. */ + if (PAM_SUCCESS != (retval = pam_open_session(pamh, 0))) + goto pamerr; + + /* Close the session. */ + if (PAM_SUCCESS != (retval = pam_close_session(pamh, 0))) + goto pamerr; + +pamerr: + /* Print an error, if needed. */ + if (PAM_SUCCESS != retval) + fprintf(stderr, "%s: %s\n", progname, pam_strerror(pamh, retval)); + + /* Terminate PAM. */ + pam_end(pamh, retval); + return retval; +} + +#endif /* INFO_PAM */ diff --git a/system_cmds/passwd.tproj/passwd.1 b/system_cmds/passwd.tproj/passwd.1 new file mode 100644 index 0000000..fd1cef1 --- /dev/null +++ b/system_cmds/passwd.tproj/passwd.1 @@ -0,0 +1,134 @@ +.\" Copyright (c) 1990, 1993 +.\" The Regents of the University of California. All rights reserved. +.\" +.\" Redistribution and use in source and binary forms, with or without +.\" modification, are permitted provided that the following conditions +.\" are met: +.\" 1. Redistributions of source code must retain the above copyright +.\" notice, this list of conditions and the following disclaimer. +.\" 2. Redistributions in binary form must reproduce the above copyright +.\" notice, this list of conditions and the following disclaimer in the +.\" documentation and/or other materials provided with the distribution. +.\" 3. All advertising materials mentioning features or use of this software +.\" must display the following acknowledgement: +.\" This product includes software developed by the University of +.\" California, Berkeley and its contributors. +.\" 4. Neither the name of the University nor the names of its contributors +.\" may be used to endorse or promote products derived from this software +.\" without specific prior written permission. +.\" +.\" THIS SOFTWARE IS PROVIDED BY THE REGENTS AND CONTRIBUTORS ``AS IS'' AND +.\" ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE +.\" IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE +.\" ARE DISCLAIMED. IN NO EVENT SHALL THE REGENTS OR CONTRIBUTORS BE LIABLE +.\" FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL +.\" DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS +.\" OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) +.\" HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT +.\" LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY +.\" OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF +.\" SUCH DAMAGE. +.\" +.\" @(#)passwd.1 8.1 (Berkeley) 6/6/93 +.\" +.Dd August 18, 2008 +.Dt PASSWD 1 +.Os "Mac OS X" +.Sh NAME +.Nm passwd +.Nd modify a user's password +.Sh SYNOPSIS +.Nm passwd +.Op Fl i Ar infosystem Op Fl l Ar location +.Op Fl u Ar authname +.Op Ar user +.Sh DESCRIPTION +The +.Nm +utility changes the user's password. +If the user is not the super-user, +.Nm +first prompts for the current password and will not continue unless the correct +password is entered. +.Pp +When entering the new password, the characters entered do not echo, in order to +avoid the password being seen by a passer-by. +The +.Nm +utility prompts for the new password twice in order to detect typing errors. +.Pp +The new password should be at least six characters long +and not purely alphabetic. +Its total length should be less than +.Dv _PASSWORD_LEN +(currently 128 characters), +although some directory systems allow longer passwords. +Numbers, upper +case letters, and meta characters are encouraged. +.Pp +Once the password has been verified, +.Nm +communicates the new password to the directory system. +.Bl -tag -width flag +.It Fl i Ar infosystem +This option specifies where the password update should be applied. +Under Mac OS X 10.5 and later, supported directory systems are: +.Bl -tag -width flag +.It Ar PAM +(default) Pluggable Authentication Modules. +.It Ar opendirectory +A system conforming to Open Directory APIs and supporting updates +(including LDAP, etc). +If no -l option is specified, the search node is used. +.It Ar file +The local flat-files (included for legacy configurations). +.It Ar nis +A remote NIS server containing the user's password. +.El +.It Fl l Ar location +This option causes the password to be updated in the given location +of the chosen directory system. +.Bl -tag -width flag +.It for file, +location may be a file name (/etc/master.passwd is the default) +.It for nis, +location may be a NIS domainname +.It for opendirectory, +location may be a directory node name +.It for PAM, +location is not used +.El +.It Fl u Ar authname +This option specifies the user name to use when authenticating to +the directory node. +.It Ar user +This optional argument specifies the user account whose password will be +changed. This account's current password may be required, even when run as the +super-user, depending on the directory system. +.El +.Sh FILES +.Bl -tag -width /etc/master.passwd -compact +.It Pa /etc/master.passwd +The user database +.It Pa /etc/passwd +A Version 7 format password file +.It Pa /etc/passwd.XXXXXX +Temporary copy of the password file +.El +.Sh SEE ALSO +.Xr chpass 1 , +.Xr login 1 , +.Xr dscl 1 , +.Xr passwd 5 , +.Xr pwd_mkdb 8 , +.Xr vipw 8 +.Rs +.%A Robert Morris +.%A Ken Thompson +.%T "UNIX password security" +.Re +.Sh HISTORY +A +.Nm passwd +command appeared in +.At v6 . diff --git a/system_cmds/passwd.tproj/passwd.c b/system_cmds/passwd.tproj/passwd.c new file mode 100644 index 0000000..877036e --- /dev/null +++ b/system_cmds/passwd.tproj/passwd.c @@ -0,0 +1,302 @@ +/* + * Copyright (c) 1999-2016 Apple Inc. All rights reserved. + * + * @APPLE_LICENSE_HEADER_START@ + * + * This file contains Original Code and/or Modifications of Original Code + * as defined in and that are subject to the Apple Public Source License + * Version 2.0 (the 'License'). You may not use this file except in + * compliance with the License. Please obtain a copy of the License at + * http://www.opensource.apple.com/apsl/ and read it before using this + * file. + * + * The Original Code and all software distributed under the License are + * distributed on an 'AS IS' basis, WITHOUT WARRANTY OF ANY KIND, EITHER + * EXPRESS OR IMPLIED, AND APPLE HEREBY DISCLAIMS ALL SUCH WARRANTIES, + * INCLUDING WITHOUT LIMITATION, ANY WARRANTIES OF MERCHANTABILITY, + * FITNESS FOR A PARTICULAR PURPOSE, QUIET ENJOYMENT OR NON-INFRINGEMENT. + * Please see the License for the specific language governing rights and + * limitations under the License. + * + * @APPLE_LICENSE_HEADER_END@ + */ +#include <TargetConditionals.h> + +#define _PASSWD_FILE "/etc/master.passwd" + +#include <stdio.h> +#include <errno.h> +#include <pwd.h> +#include <libc.h> +#include <ctype.h> +#include <string.h> +#include "passwd.h" + +#ifdef __SLICK__ +#define _PASSWORD_LEN 8 +#endif + +char* progname = "passwd"; + +static char *saltchars = "abcdefghijklmnopqrstuvwxyzABCDEFGHIJKLMNOPQRSTUVWXYZ0123456789./"; + +void +getpasswd(char *name, int isroot, int minlen, int mixcase, int nonalpha, + char *old_pw, char **new_pw, char **old_clear, char **new_clear) +{ + int i, tries, pw_ok, upper, lower, alpha, notalpha; + size_t len; + int isNull; + char *p; + static char obuf[_PASSWORD_LEN+1]; + static char nbuf[_PASSWORD_LEN+1]; + char salt[9]; + + printf("Changing password for %s.\n", name); + + p = ""; + isNull = 0; + if (old_pw == NULL) isNull = 1; + if ((isNull == 0) && (old_pw[0] == '\0')) isNull = 1; + if ((isroot == 0) && (isNull == 0)) + { + p = getpass("Old password:"); + if (strcmp(crypt(p, old_pw), old_pw)) + { + errno = EACCES; + fprintf(stderr, "Sorry\n"); + exit(1); + } + } + //strcpy(obuf, p); + snprintf( obuf, sizeof(obuf), "%s", p ); + + tries = 0; + nbuf[0] = '\0'; + for (;;) + { + p = getpass("New password:"); + if (!*p) + { + printf("Password unchanged.\n"); + exit(0); + } + + tries++; + len = strlen(p); + upper = 0; + lower = 0; + alpha = 0; + notalpha = 0; + for (i = 0; i < len; i++) + { + if (isupper(p[i])) upper++; + if (islower(p[i])) lower++; + if (isalpha(p[i])) alpha++; + else notalpha++; + } + + + pw_ok = 1; + if (len < minlen) pw_ok = 0; + if ((mixcase == 1) && ((upper == 0) || (lower == 0))) pw_ok = 0; + if ((nonalpha == 1) && (notalpha == 0)) pw_ok = 0; + + /* + * An insistent root may override security options. + */ + if ((isroot == 1) && (tries > 2)) pw_ok = 1; + + /* + * A very insistent user may override security options. + */ + if (tries > 4) pw_ok = 1; + + if (pw_ok == 0) + { + if (len < minlen) + printf("Password must be at least %d characters long.\n", minlen); + if ((mixcase == 1) && ((upper == 0) || (lower == 0))) + printf("Password must contain both upper and lower case characters.\n"); + if ((nonalpha == 1) && (notalpha == 0)) + printf("Password must contain non-alphabetic characters.\n"); + continue; + } + + //strcpy(nbuf, p); + snprintf( nbuf, sizeof(nbuf), "%s", p ); + + if (!strcmp(nbuf, getpass("Retype new password:"))) break; + + printf("Mismatch; try again, EOF to quit.\n"); + } + + /* + * Create a random salt + */ + srandom((int)time((time_t *)NULL)); + salt[0] = saltchars[random() % strlen(saltchars)]; + salt[1] = saltchars[random() % strlen(saltchars)]; + salt[2] = '\0'; + *new_pw = crypt(nbuf, salt); + + *old_clear = obuf; + *new_clear = nbuf; + return; +} + +static void +usage(void) +{ + fprintf(stderr, "usage: %s [-i infosystem] -l location]] [-u authname] [name]\n", progname); + fprintf(stderr, " infosystem:\n"); + fprintf(stderr, " file\n"); + fprintf(stderr, " NIS\n"); + fprintf(stderr, " OpenDirectory\n"); + fprintf(stderr, " PAM\n"); + fprintf(stderr, " location (for infosystem):\n"); + fprintf(stderr, " file location is path to file (default is %s)\n", _PASSWD_FILE); + fprintf(stderr, " NIS location is NIS domain name\n"); + fprintf(stderr, " OpenDirectory location is directory node name\n"); + fprintf(stderr, " PAM location is not used\n"); + exit(1); +} + +int +main(int argc, char *argv[]) +{ + char* user = NULL; + char* locn = NULL; + char* auth = NULL; + int infosystem, ch; + int free_user = 0; + int res = -1; + +#ifdef INFO_PAM + infosystem = INFO_PAM; +#else +#ifdef INFO_OPEN_DIRECTORY + infosystem = INFO_OPEN_DIRECTORY; +#else + infosystem = INFO_FILE; +#endif +#endif + +#ifdef INFO_OPEN_DIRECTORY + /* PAM is the default infosystem, but we still want to use OpenDirectory directly when run by root */ + if (0 == getuid()) + infosystem = INFO_OPEN_DIRECTORY; +#endif + + while ((ch = getopt(argc, argv, "i:l:u:")) != -1) + switch(ch) { + case 'i': + if (!strcasecmp(optarg, "file")) { + infosystem = INFO_FILE; +#ifdef INFO_NIS + } else if (!strcasecmp(optarg, "NIS")) { + infosystem = INFO_NIS; + } else if (!strcasecmp(optarg, "YP")) { + infosystem = INFO_NIS; +#endif +#ifdef INFO_OPEN_DIRECTORY + } else if (!strcasecmp(optarg, "opendirectory")) { + infosystem = INFO_OPEN_DIRECTORY; +#endif +#ifdef INFO_PAM + } else if (!strcasecmp(optarg, "PAM")) { + infosystem = INFO_PAM; +#endif + } else { + fprintf(stderr, "%s: Unknown info system \'%s\'.\n", + progname, optarg); + usage(); + } + break; + case 'l': + locn = optarg; + break; + case 'u': + auth = optarg; + break; + case '?': + default: + usage(); + break; + } + argc -= optind; + argv += optind; + + if (argc > 1) { + usage(); + } else if (argc == 1) { + user = argv[0]; + } + +#ifdef INFO_PAM + if (INFO_PAM == infosystem && NULL != locn) + usage(); +#endif + + if (user == NULL) + { + /* + * Verify that the login name exists. + * lukeh 24 Dec 1997 + */ + + /* getlogin() is the wrong thing to use here because it returns the wrong user after su */ + /* sns 5 Jan 2005 */ + + struct passwd * userRec = getpwuid(getuid()); + if (userRec != NULL && userRec->pw_name != NULL) { + /* global static mem is volatile; must strdup */ + user = strdup(userRec->pw_name); + free_user = 1; + } + + if (user == NULL) + { + fprintf(stderr, "you don't have a login name\n"); + exit(1); + } + } + + switch (infosystem) + { + case INFO_FILE: + res = file_passwd(user, locn); + break; +#ifdef INFO_NIS + case INFO_NIS: + res = nis_passwd(user, locn); + break; +#endif +#ifdef INFO_OPEN_DIRECTORY + case INFO_OPEN_DIRECTORY: + res = od_passwd(user, locn, auth); + break; +#endif +#ifdef INFO_PAM + case INFO_PAM: + res = pam_passwd(user); + break; +#endif + } + + if (res == 0) + { + printf("\n"); + printf("################################### WARNING ###################################\n"); + printf("# This tool does not update the login keychain password. #\n"); + printf("# To update it, run `security set-keychain-password` as the user in question, #\n"); + printf("# or as root providing a path to such user's login keychain. #\n"); + printf("###############################################################################\n"); + printf("\n"); + } + + if (free_user == 1) + free(user); + + exit(0); +} diff --git a/system_cmds/passwd.tproj/passwd.entitlements b/system_cmds/passwd.tproj/passwd.entitlements new file mode 100644 index 0000000..a63c168 --- /dev/null +++ b/system_cmds/passwd.tproj/passwd.entitlements @@ -0,0 +1,12 @@ +<?xml version="1.0" encoding="UTF-8"?> +<!DOCTYPE plist PUBLIC "-//Apple//DTD PLIST 1.0//EN" "http://www.apple.com/DTDs/PropertyList-1.0.dtd"> +<plist version="1.0"> +<dict> + <key>com.apple.keystore.console</key> + <true/> + <key>com.apple.private.opendirectoryd.identity</key> + <true/> + <key>com.apple.private.security.clear-library-validation</key> + <true/> +</dict> +</plist> diff --git a/system_cmds/passwd.tproj/passwd.h b/system_cmds/passwd.tproj/passwd.h new file mode 100644 index 0000000..60109db --- /dev/null +++ b/system_cmds/passwd.tproj/passwd.h @@ -0,0 +1,47 @@ +/* + * Copyright (c) 2011-2019 Apple Inc. All rights reserved. + * + * @APPLE_LICENSE_HEADER_START@ + * + * "Portions Copyright (c) 1999 Apple Computer, Inc. All Rights + * Reserved. This file contains Original Code and/or Modifications of + * Original Code as defined in and that are subject to the Apple Public + * Source License Version 1.0 (the 'License'). You may not use this file + * except in compliance with the License. Please obtain a copy of the + * License at http://www.apple.com/publicsource and read it before using + * this file. + * + * The Original Code and all software distributed under the License are + * distributed on an 'AS IS' basis, WITHOUT WARRANTY OF ANY KIND, EITHER + * EXPRESS OR IMPLIED, AND APPLE HEREBY DISCLAIMS ALL SUCH WARRANTIES, + * INCLUDING WITHOUT LIMITATION, ANY WARRANTIES OF MERCHANTABILITY, + * FITNESS FOR A PARTICULAR PURPOSE OR NON-INFRINGEMENT. Please see the + * License for the specific language governing rights and limitations + * under the License." + * + * @APPLE_LICENSE_HEADER_END@ + */ + +#include <TargetConditionals.h> + +#define INFO_FILE 1 +#if !(TARGET_OS_IPHONE && !TARGET_OS_SIMULATOR) +#define INFO_NIS 2 +#define INFO_OPEN_DIRECTORY 3 +#define INFO_PAM 4 +#endif + +extern int file_passwd(char *, char *); +#ifdef INFO_NIS +extern int nis_passwd(char *, char *); +#endif +#ifdef INFO_OPEN_DIRECTORY +extern int od_passwd(char *, char *, char*); +#endif +#ifdef INFO_PAM +extern int pam_passwd(char *); +#endif + +void +getpasswd(char *name, int isroot, int minlen, int mixcase, int nonalpha, + char *old_pw, char **new_pw, char **old_clear, char **new_clear); diff --git a/system_cmds/passwd.tproj/passwd.pam b/system_cmds/passwd.tproj/passwd.pam new file mode 100644 index 0000000..9ac660f --- /dev/null +++ b/system_cmds/passwd.tproj/passwd.pam @@ -0,0 +1,5 @@ +# passwd: auth account +auth required pam_permit.so +account required pam_opendirectory.so +password required pam_opendirectory.so +session required pam_permit.so |