2N/A/*
2N/A * CDDL HEADER START
2N/A *
2N/A * The contents of this file are subject to the terms of the
2N/A * Common Development and Distribution License (the "License").
2N/A * You may not use this file except in compliance with the License.
2N/A *
2N/A * You can obtain a copy of the license at usr/src/OPENSOLARIS.LICENSE
2N/A * or http://www.opensolaris.org/os/licensing.
2N/A * See the License for the specific language governing permissions
2N/A * and limitations under the License.
2N/A *
2N/A * When distributing Covered Code, include this CDDL HEADER in each
2N/A * file and include the License file at usr/src/OPENSOLARIS.LICENSE.
2N/A * If applicable, add the following below this CDDL HEADER, with the
2N/A * fields enclosed by brackets "[]" replaced with your own identifying
2N/A * information: Portions Copyright [yyyy] [name of copyright owner]
2N/A *
2N/A * CDDL HEADER END
2N/A */
2N/A
2N/A/*
2N/A * Copyright (c) 2003, 2011, Oracle and/or its affiliates. All rights reserved.
2N/A */
2N/A
2N/A/*
2N/A * DESCRIPTION: This is the N2L equivalent of changepasswd.c. The traditional
2N/A * version modifies the NIS source files and then initiates a
2N/A * ypmake to make the maps and push them.
2N/A *
2N/A * For N2L there are no source files and the policy is that the
2N/A * definitive information is that contained in the DIT. Old
2N/A * information is read from LDAP. Assuming this authenticates, and
2N/A * the change is acceptable, this information is modified and
2N/A * written back to LDAP.
2N/A *
2N/A * Related map entries are then found and updated finally
2N/A * yppushes of the changed maps are initiated. Since the
2N/A * definitive information has already correctly been updated the
2N/A * code is tolerant of some errors during this operation.
2N/A *
2N/A * What was previously in the maps is irrelevant.
2N/A *
2N/A * Some less than perfect code (like inline constants for
2N/A * return values and a few globals) is retained from the original.
2N/A */
2N/A
2N/A#include <sys/types.h>
2N/A#include <sys/stat.h>
2N/A#include <ctype.h>
2N/A#include <unistd.h>
2N/A#include <stdlib.h>
2N/A#include <string.h>
2N/A#include <stdio.h>
2N/A#include <errno.h>
2N/A#include <syslog.h>
2N/A#include <pwd.h>
2N/A#include <signal.h>
2N/A#include <crypt.h>
2N/A#include <rpc/rpc.h>
2N/A#include <rpcsvc/yppasswd.h>
2N/A#include <utmpx.h>
2N/A#include <shadow.h>
2N/A
2N/A#include <ndbm.h>
2N/A/* DO NOT INCLUDE SHIM_HOOKS.H */
2N/A#include "shim.h"
2N/A#include "yptol.h"
2N/A#include "../ldap_util.h"
2N/A
2N/A/* Constants */
2N/A#define CRYPTPWSIZE CRYPT_MAXCIPHERTEXTLEN
2N/A#define STRSIZE 100
2N/A#define FINGERSIZE (4 * STRSIZE - 4)
2N/A#define SHELLSIZE (STRSIZE - 2)
2N/A
2N/A#define UTUSERLEN (sizeof (((struct utmpx *)0)->ut_user))
2N/A#define COLON_CHAR ':'
2N/A
2N/A/*
2N/A * Path to DBM files. This is only required for N2L mode. Traditional mode
2N/A * works with the source files and uses the NIS Makefile to generate the maps.
2N/A * Seems to be hard coded in the rest of NIS so same is done here.
2N/A */
2N/A#define YPDBPATH "/var/yp"
2N/A
2N/A/* Names of password and adjunct mappings. Used to access DIT */
2N/A#define BYNAME ".byname"
2N/A#define BYUID ".byuid"
2N/A#define BYGID ".bygid"
2N/A#define PASSWD_MAPPING "passwd" BYNAME
2N/A#define PASSWD_ADJUNCT_MAPPING "passwd.adjunct" BYNAME
2N/A#define AGEING_MAPPING "ageing" BYNAME
2N/A
2N/A/* Bitmasks used in list of fields to change */
2N/A#define CNG_PASSWD 0x0001
2N/A#define CNG_SH 0x0002
2N/A#define CNG_GECOS 0x0004
2N/A
2N/A/* Globals :-( */
2N/Aextern int single, nogecos, noshell, nopw, mflag;
2N/A
2N/A/*
2N/A * Structure for containing the information is currently in the DIT. This is
2N/A * similar to the passwd structure defined in getpwent(3C) apart from.
2N/A *
2N/A * 1. Since GID and UID are never changed they are not converted to integers.
2N/A * 2. There are extra fields to hold adjunct information.
2N/A * 3. There are extra fields to hold widely used information.
2N/A */
2N/Astruct passwd_entry {
2N/A char *pw_name;
2N/A char *pw_passwd;
2N/A char *pw_uid;
2N/A char *pw_gid;
2N/A char *pw_gecos;
2N/A char *pw_dir;
2N/A char *pw_shell;
2N/A char *adjunct_tail; /* Tail of adjunct entry (opaque) */
2N/A bool_t adjunct; /* Flag indicating if DIT has adjunct info */
2N/A char *pwd_str; /* New password string */
2N/A char *adjunct_str; /* New adjunct string */
2N/A};
2N/A
2N/A/* Prototypes */
2N/Aextern bool_t validloginshell(char *sh, char *arg, int);
2N/Aextern int validstr(char *str, size_t size);
2N/A
2N/Asuc_code write_shadow_info(char *, struct spwd *);
2N/Aint put_new_info(struct passwd_entry *, char *);
2N/Achar *create_pwd_str(struct passwd_entry *, bool_t);
2N/Aint proc_domain(struct yppasswd *, bool_t, char *);
2N/Aint proc_request(struct yppasswd *, struct passwd_entry *, bool_t, char *);
2N/Aint modify_ent(struct yppasswd *, struct passwd_entry *t, bool_t, char *);
2N/Aint get_change_list(struct yppasswd *, struct passwd_entry *);
2N/Astruct passwd_entry *get_old_info(char *, char *);
2N/Astatic char *get_next_token(char *, char **, char *);
2N/Avoid free_pwd_entry(struct passwd_entry *);
2N/Astruct spwd *get_old_shadow(char *, char *);
2N/Asuc_code decode_shadow_entry(datum *, struct spwd *);
2N/Avoid free_shadow_entry(struct spwd *);
2N/Aint proc_maps(char *, struct passwd_entry *);
2N/Aint proc_map_list(char **, char *, struct passwd_entry *, bool_t);
2N/Aint update_single_map(char *, struct passwd_entry *, bool_t);
2N/Abool_t strend(char *s1, char *s2);
2N/A
2N/A/*
2N/A * FUNCTION: shim_changepasswd()
2N/A *
2N/A * DESCRIPTION: N2L version of changepasswd(). When this is called 'useshadow'
2N/A * etc. will have been set up but are meaningless. We work out
2N/A * what to change based on information from the DIT.
2N/A *
2N/A * INPUTS: Identical to changepasswd()
2N/A *
2N/A * OUTPUTS: Identical to changepasswd()
2N/A */
2N/Avoid
2N/Ashim_changepasswd(SVCXPRT *transp)
2N/A{
2N/A struct yppasswd yppwd;
2N/A bool_t root_on_master = FALSE;
2N/A char domain[MAXNETNAMELEN+1];
2N/A char **domain_list;
2N/A int dom_count, i;
2N/A
2N/A int ret, ans = 2; /* Answer codes */
2N/A
2N/A /* Clean out yppwd ... maybe we don't trust RPC */
2N/A memset(&yppwd, 0, sizeof (struct yppasswd));
2N/A
2N/A /* Get the RPC args */
2N/A if (!svc_getargs(transp, xdr_yppasswd, (caddr_t)&yppwd)) {
2N/A svcerr_decode(transp);
2N/A return;
2N/A }
2N/A
2N/A /* Perform basic validation */
2N/A if ((!validstr(yppwd.newpw.pw_passwd, CRYPTPWSIZE)) ||
2N/A (!validstr(yppwd.newpw.pw_name, UTUSERLEN)) ||
2N/A (!validstr(yppwd.newpw.pw_gecos, FINGERSIZE)) ||
2N/A (!validstr(yppwd.newpw.pw_shell, SHELLSIZE))) {
2N/A svcerr_decode(transp);
2N/A return;
2N/A }
2N/A
2N/A /*
2N/A * Special case: root on the master server can change other
2N/A * users' passwords without first entering the old password.
2N/A * We need to ensure that this is indeed root on the master
2N/A * server. (bug 1253949)
2N/A */
2N/A if (strcmp(transp->xp_netid, "ticlts") == 0) {
2N/A svc_local_cred_t cred;
2N/A if (!svc_get_local_cred(transp, &cred)) {
2N/A logmsg(MSG_NOTIMECHECK, LOG_ERR,
2N/A "Couldn't get local user credentials");
2N/A } else if (cred.ruid == 0)
2N/A root_on_master = TRUE;
2N/A }
2N/A
2N/A /*
2N/A * Get the domain name. This is tricky because a N2L server may be
2N/A * handling multiple domains. There is nothing in the request to
2N/A * indicate which one we are trying to change a passwd for. First
2N/A * we try to get a list of password related domains from the mapping
2N/A * file.
2N/A */
2N/A if (0 !=
2N/A (dom_count = get_mapping_yppasswdd_domain_list(&domain_list))) {
2N/A /* Got a domain list ... process all the domains */
2N/A for (i = 0; i < dom_count; i ++) {
2N/A ret = proc_domain(&yppwd, root_on_master,
2N/A domain_list[i]);
2N/A
2N/A /* If one has worked don't care if others fail */
2N/A if (0 != ans)
2N/A ans = ret;
2N/A }
2N/A }
2N/A else
2N/A {
2N/A /*
2N/A * There was no domain list in the mapping file. The
2N/A * traditional version of this code calls ypmake which picks
2N/A * up the domain returned by getdomainname(). Fall back to the
2N/A * same mechanism.
2N/A */
2N/A if (0 > getdomainname(domain, MAXNETNAMELEN+1)) {
2N/A logmsg(MSG_NOTIMECHECK, LOG_ERR,
2N/A "Could not get any domain info");
2N/A } else {
2N/A /* Got one domain ... process it. */
2N/A ans = proc_domain(&yppwd, root_on_master, domain);
2N/A }
2N/A }
2N/A
2N/A /* Send reply packet */
2N/A if (!svc_sendreply(transp, xdr_int, (char *)&ans))
2N/A logmsg(MSG_NOTIMECHECK, LOG_WARNING,
2N/A "could not reply to RPC call");
2N/A}
2N/A
2N/A/*
2N/A * FUNCTION : proc_domain()
2N/A *
2N/A * DESCRIPTION: Process a request for one domain
2N/A *
2N/A * GIVEN : Pointer to the request.
2N/A * Root on master flag
2N/A * Domain
2N/A *
2N/A * OUTPUTS : Answer code for reply
2N/A */
2N/Aint
2N/Aproc_domain(struct yppasswd *yppwd, bool_t root_on_master, char *domain)
2N/A{
2N/A struct passwd_entry *old_pwd;
2N/A char *p;
2N/A int ans = 2;
2N/A
2N/A /* security hole fix from original source */
2N/A for (p = yppwd->newpw.pw_name; (*p != '\0'); p++)
2N/A if ((*p == ':') || !(isprint(*p)))
2N/A *p = '$'; /* you lose buckwheat */
2N/A for (p = yppwd->newpw.pw_passwd; (*p != '\0'); p++)
2N/A if ((*p == ':') || !(isprint(*p)))
2N/A *p = '$'; /* you lose buckwheat */
2N/A
2N/A /* Get old info from DIT for this domain */
2N/A old_pwd = get_old_info(yppwd->newpw.pw_name, domain);
2N/A if (NULL == old_pwd) {
2N/A logmsg(MSG_NOTIMECHECK, LOG_ERR,
2N/A "Could not get old information for %s in "
2N/A "domain %s", yppwd->newpw.pw_name, domain);
2N/A return (ans);
2N/A }
2N/A
2N/A /* Have a request that can be replied to */
2N/A ans = proc_request(yppwd, old_pwd, root_on_master, domain);
2N/A free_pwd_entry(old_pwd);
2N/A
2N/A return (ans);
2N/A}
2N/A
2N/A/*
2N/A * FUNCTION : proc_request()
2N/A *
2N/A * DESCRIPTION: Process a request
2N/A *
2N/A * GIVEN : Pointer to the request.
2N/A * Pointer to old information from LDAP
2N/A * Root on master flag
2N/A * Domain
2N/A *
2N/A * OUTPUTS : Answer code for reply
2N/A */
2N/Aint
2N/Aproc_request(struct yppasswd *yppwd, struct passwd_entry *old_pwd,
2N/A bool_t root_on_master, char *domain)
2N/A{
2N/A struct sigaction sa, osa1, osa2, osa3;
2N/A int ans;
2N/A
2N/A /* Authenticate */
2N/A if ((0 != strcmp(crypt(yppwd->oldpass, old_pwd->pw_passwd),
2N/A old_pwd->pw_passwd)) && !root_on_master) {
2N/A logmsg(MSG_NOTIMECHECK, LOG_NOTICE, "Passwd incorrect %s",
2N/A yppwd->newpw.pw_name);
2N/A return (7);
2N/A }
2N/A
2N/A /* Work out what we have to change and change it */
2N/A ans = modify_ent(yppwd, old_pwd, root_on_master, domain);
2N/A if (0 != ans)
2N/A return (ans);
2N/A
2N/A /*
2N/A * Generate passwd and adjunct map entries. This creates extra
2N/A * malloced strings in old_pwd. These will be freed when
2N/A * free_pwd_entry() is called to free up the rest of the structure.
2N/A */
2N/A old_pwd->pwd_str = create_pwd_str(old_pwd, FALSE);
2N/A if (NULL == old_pwd->pwd_str) {
2N/A logmsg(MSG_NOTIMECHECK, LOG_ERR,
2N/A "Could not create passwd entry");
2N/A return (2);
2N/A }
2N/A if (old_pwd->adjunct) {
2N/A old_pwd->adjunct_str = create_pwd_str(old_pwd, TRUE);
2N/A if (NULL == old_pwd->adjunct_str) {
2N/A logmsg(MSG_NOTIMECHECK, LOG_ERR,
2N/A "Could not create adjunct entry");
2N/A return (2);
2N/A }
2N/A } else {
2N/A old_pwd->adjunct_str = NULL;
2N/A }
2N/A
2N/A /* Put the information back to DIT */
2N/A ans = put_new_info(old_pwd, domain);
2N/A if (0 != ans) {
2N/A return (ans);
2N/A }
2N/A
2N/A /* Are going to be forking pushes, set up signals */
2N/A memset(&sa, 0, sizeof (struct sigaction));
2N/A sa.sa_handler = SIG_IGN;
2N/A sigaction(SIGTSTP, &sa, (struct sigaction *)0);
2N/A sigaction(SIGHUP, &sa, &osa1);
2N/A sigaction(SIGINT, &sa, &osa2);
2N/A sigaction(SIGQUIT, &sa, &osa3);
2N/A
2N/A /* Update and push all the maps */
2N/A ans = proc_maps(domain, old_pwd);
2N/A
2N/A /* Tidy up signals */
2N/A sigaction(SIGHUP, &osa1, (struct sigaction *)0);
2N/A sigaction(SIGINT, &osa2, (struct sigaction *)0);
2N/A sigaction(SIGQUIT, &osa3, (struct sigaction *)0);
2N/A
2N/A return (ans);
2N/A}
2N/A
2N/A/*
2N/A * FUNCTION: proc_maps()
2N/A *
2N/A * DESCRIPTION: Gets all the map lists and processes them.
2N/A *
2N/A * INPUTS: Domain name
2N/A * New info to write into maps
2N/A *
2N/A * OUTPUT : Answer code
2N/A */
2N/Aint
2N/Aproc_maps(char *domain, struct passwd_entry *pwd)
2N/A{
2N/A char **map_list; /* Array of passwd or adjunct maps */
2N/A int ans = 0;
2N/A
2N/A /* Get list of passwd maps from mapping file */
2N/A map_list = get_passwd_list(FALSE, domain);
2N/A if (map_list != NULL) {
2N/A /* Process list of passwd maps */
2N/A ans = proc_map_list(map_list, domain, pwd, FALSE);
2N/A free_passwd_list(map_list);
2N/A if (0 != ans)
2N/A return (ans);
2N/A }
2N/A
2N/A /*
2N/A * If we get here either there were no passwd maps or there were
2N/A * some and they were processed successfully. Either case is good
2N/A * continue and process passwd.adjunct maps.
2N/A */
2N/A
2N/A /* Get list of adjunct maps from mapping file */
2N/A map_list = get_passwd_list(TRUE, domain);
2N/A if (map_list != NULL) {
2N/A /*
2N/A * Process list of adjunct maps. If the required information
2N/A * is not present in LDAP then the updates attempts will log
2N/A * an error. No need to make the check here
2N/A */
2N/A ans = proc_map_list(map_list, domain, pwd, TRUE);
2N/A free_passwd_list(map_list);
2N/A }
2N/A
2N/A return (ans);
2N/A}
2N/A
2N/A/*
2N/A * FUNCTION: proc_map_list()
2N/A *
2N/A * DESCRIPTION: Finds entries in one list of map that need to be updated.
2N/A * updates them and writes them back.
2N/A *
2N/A * INPUTS: Null terminated list of maps to process.
2N/A * Domain name
2N/A * Information to write (including user name)
2N/A * Flag indicating if this is the adjunct list
2N/A *
2N/A * OUTPUTS: An error code
2N/A */
2N/Aint
2N/Aproc_map_list(char **map_list, char *domain,
2N/A struct passwd_entry *pwd, bool_t adjunct_flag)
2N/A{
2N/A const char *myself = "proc_map_list";
2N/A char *map_name;
2N/A char cmdbuf[BUFSIZ];
2N/A int map_name_len = 0;
2N/A int index, ans = 0;
2N/A int res;
2N/A
2N/A /* If this is a adjunct list check LDAP had some adjunct info */
2N/A if ((adjunct_flag) && (!pwd->adjunct)) {
2N/A logmsg(MSG_NOTIMECHECK, LOG_INFO,
2N/A "Have adjunct map list but no adjunct data in DIT");
2N/A /* Not a disaster */
2N/A return (0);
2N/A }
2N/A
2N/A /* Allocate enough buffer to take longest map name */
2N/A for (index = 0; map_list[index] != NULL; index ++)
2N/A if (map_name_len < strlen(map_list[index]))
2N/A map_name_len = strlen(map_list[index]);
2N/A map_name_len += strlen(YPDBPATH);
2N/A map_name_len += strlen(NTOL_PREFIX);
2N/A map_name_len += strlen(domain);
2N/A map_name_len += 3;
2N/A if (NULL == (map_name = am(myself, map_name_len))) {
2N/A logmsg(MSG_NOMEM, LOG_ERR, "Could not alloc map name");
2N/A return (2);
2N/A }
2N/A
2N/A /* For all maps in list */
2N/A for (index = 0; map_list[index] != NULL; index ++) {
2N/A
2N/A /* Generate full map name */
2N/A strcpy(map_name, YPDBPATH);
2N/A add_separator(map_name);
2N/A strcat(map_name, domain);
2N/A add_separator(map_name);
2N/A strcat(map_name, NTOL_PREFIX);
2N/A strcat(map_name, map_list[index]);
2N/A
2N/A if (0 != (ans = update_single_map(map_name, pwd, adjunct_flag)))
2N/A break;
2N/A }
2N/A
2N/A /* Done with full map path */
2N/A sfree(map_name);
2N/A
2N/A /*
2N/A * If (ans != 0) then one more maps have failed. LDAP has however been
2N/A * updates. This is the definitive source for information there is no
2N/A * need to unwind. (This was probably due to maps that were already
2N/A * corrupt).
2N/A */
2N/A
2N/A /*
2N/A * If it all worked fork off push operations for the maps. Since we
2N/A * want the map to end up with it's traditional name on the slave send
2N/A * the name without its LDAP_ prefix. The slave will call ypxfrd
2N/A * which, since it is running in N2L mode, will put the prefix back on
2N/A * before reading the file.
2N/A */
2N/A if (mflag && (0 == ans)) {
2N/A for (index = 0; (map_name = map_list[index]) != NULL;
2N/A index ++) {
2N/A if (fork() == 0) {
2N/A /*
2N/A * Define full path to yppush. Probably also
2N/A * best for security.
2N/A */
2N/A strcpy(cmdbuf, "/usr/lib/netsvc/yp/yppush ");
2N/A strcat(cmdbuf, map_name);
2N/A if (0 > system(cmdbuf))
2N/A logmsg(MSG_NOTIMECHECK, LOG_ERR,
2N/A "Could not initiate yppush");
2N/A exit(0);
2N/A }
2N/A }
2N/A }
2N/A return (ans);
2N/A}
2N/A
2N/A/*
2N/A * FUNCTION : update_single_map()
2N/A *
2N/A * DESCRIPTION: Updates one map. This is messy because we want to lock the map
2N/A * to prevent other processes from updating it at the same time.
2N/A * This mandates that we open it using the shim. When we
2N/A * write to it however we DO NOT want to write through to LDAP
2N/A * i.e. do not want to use the shim.
2N/A *
2N/A * Solution : Do not include shim_hooks.h but call the shim
2N/A * versions of dbm_functions explicitly where needed.
2N/A *
2N/A * INPUT : Full name of map
2N/A * Information to write (including user name)
2N/A * Flag indicating if this is an adjunct map.
2N/A *
2N/A * OUTPUT : Answer code
2N/A *
2N/A */
2N/Aint
2N/Aupdate_single_map(char *map_name, struct passwd_entry *pwd, bool_t adjunct_flag)
2N/A{
2N/A DBM *map;
2N/A int res;
2N/A datum data, key;
2N/A
2N/A /* Set up data */
2N/A if (adjunct_flag)
2N/A data.dptr = pwd->adjunct_str;
2N/A else
2N/A data.dptr = pwd->pwd_str;
2N/A data.dsize = strlen(data.dptr);
2N/A
2N/A /* Set up key dependent on which type of map this is */
2N/A key.dptr = NULL;
2N/A if (strend(map_name, BYNAME))
2N/A key.dptr = pwd->pw_name;
2N/A if (strend(map_name, BYUID))
2N/A key.dptr = pwd->pw_uid;
2N/A if (strend(map_name, BYGID))
2N/A key.dptr = pwd->pw_gid;
2N/A
2N/A if (NULL == key.dptr) {
2N/A logmsg(MSG_NOTIMECHECK, LOG_ERR,
2N/A "Unrecognized map type %s", map_name);
2N/A return (0); /* Next map */
2N/A }
2N/A key.dsize = strlen(key.dptr);
2N/A
2N/A /* Open the map */
2N/A map = shim_dbm_open(map_name, O_RDWR, 0600);
2N/A if (NULL == map) {
2N/A logmsg(MSG_NOTIMECHECK, LOG_ERR, "Could not open %s", map_name);
2N/A return (0); /* Next map */
2N/A }
2N/A
2N/A /* Lock map for update. Painful and may block but have to do it */
2N/A if (SUCCESS != lock_map_update((map_ctrl *)map)) {
2N/A logmsg(MSG_NOTIMECHECK, LOG_ERR,
2N/A "Could not lock map %s for update", map_name);
2N/A shim_dbm_close(map);
2N/A return (2);
2N/A }
2N/A
2N/A /* Do the update use simple DBM operation */
2N/A res = dbm_store(((map_ctrl *)map)->entries, key, data, DBM_REPLACE);
2N/A
2N/A /* update entry TTL. If we fail not a problem will just timeout early */
2N/A update_entry_ttl((map_ctrl *)map, &key, TTL_RAND);
2N/A
2N/A /*
2N/A * Map has been modified so update YP_LAST_MODIFIED. In the vanilla
2N/A * NIS case this would have been done by the ypmake done after updating
2N/A * the passwd source file. If this fails not a great problem the map
2N/A */
2N/A if (FAILURE == update_timestamp(((map_ctrl *)map)->entries)) {
2N/A logmsg(MSG_NOTIMECHECK, LOG_ERR, "Could not update "
2N/A "YP_LAST_MODIFIED %s will not be pushed this time",
2N/A map_name);
2N/A }
2N/A
2N/A /*
2N/A * Possibly should hold the lock until after push is complete
2N/A * but this could deadlock if client is slow and ypxfrd also
2N/A * decides to do an update.
2N/A */
2N/A unlock_map_update((map_ctrl *)map);
2N/A
2N/A /* Close the map */
2N/A shim_dbm_close(map);
2N/A
2N/A if (0 != res) {
2N/A logmsg(MSG_NOTIMECHECK, LOG_ERR,
2N/A "Could not update map %s", map_name);
2N/A return (2);
2N/A }
2N/A
2N/A return (0);
2N/A}
2N/A
2N/A/*
2N/A * FUNCTION : strend()
2N/A *
2N/A * DESCRIPTION: Determines if one string ends with another.
2N/A */
2N/Abool_t
2N/Astrend(char *s1, char *s2)
2N/A{
2N/A int len_dif;
2N/A
2N/A len_dif = strlen(s1) - strlen(s2);
2N/A if (0 > len_dif)
2N/A return (FALSE);
2N/A if (0 == strcmp(s1 + len_dif, s2))
2N/A return (TRUE);
2N/A return (FALSE);
2N/A}
2N/A
2N/A/*
2N/A * FUNCTION: modify_ent()
2N/A *
2N/A * DESCRIPTION: Modify an entry to reflect a request.
2N/A *
2N/A * INPUT: Pointer to the request.
2N/A * Pointer to the entry to modify.
2N/A * Flag indication if we are root on master
2N/A * Domain
2N/A *
2N/A * OUTPUT: Error code
2N/A */
2N/Aint
2N/Amodify_ent(struct yppasswd *yppwd, struct passwd_entry *old_ent,
2N/A bool_t root_on_master, char *domain)
2N/A{
2N/A int change_list;
2N/A struct spwd *shadow;
2N/A time_t now;
2N/A
2N/A /* Get list of changes */
2N/A change_list = get_change_list(yppwd, old_ent);
2N/A
2N/A if (!change_list) {
2N/A logmsg(MSG_NOTIMECHECK, LOG_NOTICE,
2N/A "No change for %s", yppwd->newpw.pw_name);
2N/A return (3);
2N/A }
2N/A
2N/A /* Check that the shell we have been given is acceptable. */
2N/A if ((change_list & CNG_SH) && (!validloginshell(old_ent->pw_shell,
2N/A yppwd->newpw.pw_shell, root_on_master)))
2N/A return (2);
2N/A
2N/A /*
2N/A * If changing the password do any aging checks.
2N/A * Since there are no shadow maps this is done by accessing
2N/A * attributes in the DIT via the mapping system.
2N/A */
2N/A if (change_list & CNG_PASSWD) {
2N/A
2N/A /* Try to get shadow information */
2N/A shadow = get_old_shadow(yppwd->newpw.pw_name, domain);
2N/A
2N/A /* If there is shadow information make password aging checks */
2N/A if (NULL != shadow) {
2N/A now = DAY_NOW;
2N/A /* password aging - bug for bug compatibility */
2N/A if (shadow->sp_max != -1) {
2N/A if (now < shadow->sp_lstchg + shadow->sp_min) {
2N/A logmsg(MSG_NOTIMECHECK, LOG_ERR,
2N/A "Sorry: < %ld days since "
2N/A "the last change", shadow->sp_min);
2N/A free_shadow_entry(shadow);
2N/A return (2);
2N/A }
2N/A }
2N/A
2N/A /* Update time of change */
2N/A shadow->sp_lstchg = now;
2N/A
2N/A /* Write it back */
2N/A write_shadow_info(domain, shadow);
2N/A
2N/A free_shadow_entry(shadow);
2N/A }
2N/A }
2N/A
2N/A /* Make changes to old entity */
2N/A if (change_list & CNG_GECOS) {
2N/A if (NULL != old_ent->pw_gecos)
2N/A sfree(old_ent->pw_gecos);
2N/A old_ent->pw_gecos = strdup(yppwd->newpw.pw_gecos);
2N/A if (NULL == old_ent->pw_gecos) {
2N/A logmsg(MSG_NOMEM, LOG_ERR, "Could not allocate gecos");
2N/A return (2);
2N/A }
2N/A }
2N/A
2N/A if (change_list & CNG_SH) {
2N/A if (NULL != old_ent->pw_shell)
2N/A sfree(old_ent->pw_shell);
2N/A old_ent->pw_shell = strdup(yppwd->newpw.pw_shell);
2N/A if (NULL == old_ent->pw_shell) {
2N/A logmsg(MSG_NOMEM, LOG_ERR, "Could not allocate shell");
2N/A return (2);
2N/A }
2N/A }
2N/A
2N/A if (change_list & CNG_PASSWD) {
2N/A if (NULL != old_ent->pw_passwd)
2N/A sfree(old_ent->pw_passwd);
2N/A old_ent->pw_passwd = strdup(yppwd->newpw.pw_passwd);
2N/A if (NULL == old_ent->pw_passwd) {
2N/A logmsg(MSG_NOMEM, LOG_ERR, "Could not allocate passwd");
2N/A return (2);
2N/A }
2N/A }
2N/A
2N/A return (0);
2N/A}
2N/A
2N/A/*
2N/A * FUNCTION : get_change_list()
2N/A *
2N/A * DESCRIPTION: Works out what we have to change.
2N/A *
2N/A * INPUTS : Request.
2N/A * Structure containing current state of entry
2N/A *
2N/A * OUTPUTS : A bitmask signaling what to change. (Implemented in this
2N/A * way to make it easy to pass between functions).
2N/A */
2N/Aint
2N/Aget_change_list(struct yppasswd *yppwd, struct passwd_entry *old_ent)
2N/A{
2N/A int list = 0;
2N/A char *p;
2N/A
2N/A p = yppwd->newpw.pw_passwd;
2N/A if ((!nopw) &&
2N/A p && *p &&
2N/A !(*p++ == '#' && *p++ == '#' &&
2N/A (strcmp(p, old_ent->pw_name) == 0)) &&
2N/A (strcmp(crypt(old_ent->pw_passwd,
2N/A yppwd->newpw.pw_passwd), yppwd->newpw.pw_passwd) != 0))
2N/A list |= CNG_PASSWD;
2N/A
2N/A if ((NULL != old_ent->pw_shell) &&
2N/A (!noshell) &&
2N/A (strcmp(old_ent->pw_shell, yppwd->newpw.pw_shell) != 0)) {
2N/A if (single)
2N/A list = 0;
2N/A list |= CNG_SH;
2N/A }
2N/A
2N/A if ((NULL != old_ent->pw_gecos) &&
2N/A (!nogecos) &&
2N/A (strcmp(old_ent->pw_gecos, yppwd->newpw.pw_gecos) != 0)) {
2N/A if (single)
2N/A list = 0;
2N/A list |= CNG_GECOS;
2N/A }
2N/A
2N/A return (list);
2N/A}
2N/A
2N/A/*
2N/A * FUNCTION : decode_pwd_entry()
2N/A *
2N/A * DESCRIPTION: Pulls apart a password entry. Because the password entry has
2N/A * come from the mapping system it can be assumed to be correctly
2N/A * formatted and relatively simple parsing can be done.
2N/A *
2N/A * Substrings are put into malloced memory. Caller to free.
2N/A *
2N/A * For adjunct files most of it is left empty.
2N/A *
2N/A * It would be nice to use getpwent and friends for this work but
2N/A * these only seem to exist for files and it seems excessive to
2N/A * create a temporary file for this operation.
2N/A *
2N/A * INPUTS: Pointer to datum containing password string.
2N/A * Pointer to structure in which to return results
2N/A * Flag indicating if we are decoding passwd or passwd.adjunct
2N/A *
2N/A * OUTPUTS: SUCCESS = Decoded successfully
2N/A * FAILURE = Not decoded successfully. Caller to tidy up.
2N/A */
2N/Asuc_code
2N/Adecode_pwd_entry(datum *data, struct passwd_entry *pwd, bool_t adjunct)
2N/A{
2N/A const char *myself = "decode_pwd_entry";
2N/A char *dptr = data->dptr;
2N/A char *p, *str_end, *temp;
2N/A
2N/A /* Work out last location in string */
2N/A str_end = dptr + data->dsize;
2N/A
2N/A /* Name */
2N/A if (NULL == (p = get_next_token(dptr, &temp, str_end)))
2N/A return (FAILURE);
2N/A if (adjunct) {
2N/A /* If we found an adjunct version this is the one to use */
2N/A if (NULL != pwd->pw_name)
2N/A sfree(pwd->pw_name);
2N/A }
2N/A pwd->pw_name = temp;
2N/A
2N/A /* Password */
2N/A if (NULL == (p = get_next_token(p, &temp, str_end)))
2N/A return (FAILURE);
2N/A if (adjunct) {
2N/A /* If we found an adjunct version this is the one to use */
2N/A if (NULL != pwd->pw_passwd)
2N/A sfree(pwd->pw_passwd);
2N/A }
2N/A pwd->pw_passwd = temp;
2N/A
2N/A if (adjunct) {
2N/A /* Store adjunct information in opaque string */
2N/A pwd->adjunct_tail = am(myself, str_end - p + 1);
2N/A if (NULL == pwd->adjunct_tail)
2N/A return (FAILURE);
2N/A strncpy(pwd->adjunct_tail, p, str_end - p);
2N/A pwd->adjunct_tail[str_end - p] = '\0';
2N/A
2N/A /* Remember that LDAP contained adjunct data */
2N/A pwd->adjunct = TRUE;
2N/A return (SUCCESS);
2N/A }
2N/A
2N/A /* If we get here not adjunct. Decode rest of passwd */
2N/A
2N/A /* UID */
2N/A if (NULL == (p = get_next_token(p, &(pwd->pw_uid), str_end)))
2N/A return (FAILURE);
2N/A
2N/A /* GID */
2N/A if (NULL == (p = get_next_token(p, &(pwd->pw_gid), str_end)))
2N/A return (FAILURE);
2N/A
2N/A /* Gecos */
2N/A if (NULL == (p = get_next_token(p, &(pwd->pw_gecos), str_end)))
2N/A return (FAILURE);
2N/A
2N/A /* Home dir */
2N/A if (NULL == (p = get_next_token(p, &(pwd->pw_dir), str_end)))
2N/A return (FAILURE);
2N/A
2N/A /* Shell may not be present so don't check return */
2N/A get_next_token(p, &(pwd->pw_shell), str_end);
2N/A
2N/A if (NULL == pwd->pw_shell)
2N/A return (FAILURE);
2N/A
2N/A return (SUCCESS);
2N/A}
2N/A
2N/A/*
2N/A * FUNCTION : get_next_token()
2N/A *
2N/A * DESCRIPTION: Gets the next token from a string upto the next colon or the
2N/A * end of the string. The duplicates this token into malloced
2N/A * memory removing any spaces.
2N/A *
2N/A * INPUTS : String to search for token. NOT NULL TERMINATED
2N/A * Location to return result (NULL if result not required)
2N/A * Last location in string
2N/A *
2N/A * OUTPUT : Pointer into the string immediately after the token.
2N/A * NULL if end of string reached or error.
2N/A */
2N/Astatic char *
2N/Aget_next_token(char *str, char **op, char *str_end)
2N/A{
2N/A const char *myself = "get_next_token";
2N/A char *p, *tok_start, *tok_end;
2N/A
2N/A p = str;
2N/A /* Skip leading whitespace */
2N/A while (' ' == *p)
2N/A p++;
2N/A tok_start = p;
2N/A tok_end = p;
2N/A
2N/A while ((str_end + 1 != p) && (COLON_CHAR != *p)) {
2N/A if (' ' != *p)
2N/A tok_end = p;
2N/A p++;
2N/A }
2N/A
2N/A /* Required string is now between start and end */
2N/A if (NULL != op) {
2N/A *op = am(myself, tok_end - tok_start + 2);
2N/A if (NULL == *op) {
2N/A logmsg(MSG_NOMEM, LOG_ERR,
2N/A "Could not alloc memory for token");
2N/A return (NULL);
2N/A }
2N/A strncpy(*op, tok_start, tok_end - tok_start + 1);
2N/A
2N/A /* Terminate token */
2N/A (*op)[tok_end - tok_start + 1] = '\0';
2N/A
2N/A }
2N/A
2N/A /* Check if we reached the end of the input string */
2N/A if ('\0' == *p)
2N/A return (NULL);
2N/A
2N/A /* There is some more */
2N/A p++;
2N/A return (p);
2N/A}
2N/A
2N/A/*
2N/A * FUNCTION : free_pwd_entry()
2N/A *
2N/A * DESCRIPTION: Frees up a pwd_entry structure and its contents.
2N/A *
2N/A * INPUTS: Pointer to the structure to free.
2N/A *
2N/A * OUTPUT: Nothing
2N/A */
2N/Avoid
2N/Afree_pwd_entry(struct passwd_entry *pwd)
2N/A{
2N/A /* Free up strings */
2N/A if (NULL != pwd->pw_name)
2N/A sfree(pwd->pw_name);
2N/A
2N/A if (NULL != pwd->pw_passwd)
2N/A sfree(pwd->pw_passwd);
2N/A
2N/A if (NULL != pwd->pw_gecos)
2N/A sfree(pwd->pw_gecos);
2N/A
2N/A if (NULL != pwd->pw_shell)
2N/A sfree(pwd->pw_shell);
2N/A
2N/A if (NULL != pwd->pw_dir)
2N/A sfree(pwd->pw_dir);
2N/A
2N/A if (NULL != pwd->adjunct_tail)
2N/A sfree(pwd->adjunct_tail);
2N/A
2N/A if (NULL != pwd->pwd_str)
2N/A sfree(pwd->pwd_str);
2N/A
2N/A if (NULL != pwd->adjunct_str)
2N/A sfree(pwd->adjunct_str);
2N/A
2N/A /* Free up structure */
2N/A sfree(pwd);
2N/A}
2N/A
2N/A/*
2N/A * FUNCTION : create_pwd_str()
2N/A *
2N/A * DESCRIPTION: Builds up a new password entity string from a passwd structure.
2N/A *
2N/A * INPUTS : Structure containing password details
2N/A * Flag indicating if we should create an adjunct or passwd string.
2N/A *
2N/A * OUTPUTS : String in malloced memory (to be freed by caller).
2N/A * NULL on failure.
2N/A */
2N/Achar *
2N/Acreate_pwd_str(struct passwd_entry *pwd, bool_t adjunct)
2N/A{
2N/A const char *myself = "create_pwd_str";
2N/A char *s;
2N/A int len;
2N/A
2N/A /* Separator string so we can strcat separator onto things */
2N/A char sep_str[2] = {COLON_CHAR, '\0'};
2N/A
2N/A /* Work out the size */
2N/A len = strlen(pwd->pw_name) + 1;
2N/A len += strlen(pwd->pw_passwd) + 1;
2N/A if (adjunct) {
2N/A len += strlen(pwd->adjunct_tail) + 1;
2N/A } else {
2N/A len += strlen(pwd->pw_uid) + 1;
2N/A len += strlen(pwd->pw_gid) + 1;
2N/A len += strlen(pwd->pw_gecos) + 1;
2N/A len += strlen(pwd->pw_dir) + 1;
2N/A len += strlen(pwd->pw_shell) + 1;
2N/A }
2N/A
2N/A /* Allocate some memory for it */
2N/A s = am(myself, len);
2N/A if (NULL == s)
2N/A return (NULL);
2N/A
2N/A strcpy(s, pwd->pw_name);
2N/A strcat(s, sep_str);
2N/A if (!adjunct) {
2N/A /* Build up a passwd string */
2N/A
2N/A /* If LDAP contains adjunct info then passwd is 'x' */
2N/A if (pwd->adjunct) {
2N/A strcat(s, "##");
2N/A strcat(s, pwd->pw_name);
2N/A } else {
2N/A strcat(s, pwd->pw_passwd);
2N/A }
2N/A strcat(s, sep_str);
2N/A strcat(s, pwd->pw_uid);
2N/A strcat(s, sep_str);
2N/A strcat(s, pwd->pw_gid);
2N/A strcat(s, sep_str);
2N/A strcat(s, pwd->pw_gecos);
2N/A strcat(s, sep_str);
2N/A strcat(s, pwd->pw_dir);
2N/A strcat(s, sep_str);
2N/A strcat(s, pwd->pw_shell);
2N/A } else {
2N/A /* Build up a passwd_adjunct string */
2N/A strcat(s, pwd->pw_passwd);
2N/A strcat(s, sep_str);
2N/A strcat(s, pwd->adjunct_tail);
2N/A }
2N/A
2N/A return (s);
2N/A}
2N/A
2N/A/*
2N/A * FUNCTION: get_old_info()
2N/A *
2N/A * DESCRIPTION: Gets as much information as possible from LDAP about one user.
2N/A *
2N/A * This goes through the mapping system. This is messy because
2N/A * them mapping system will build up a password entry from the
2N/A * contents of the DIT. We then have to parse this to recover
2N/A * it's individual fields.
2N/A *
2N/A * INPUT: Pointer to user name
2N/A * Domain
2N/A *
2N/A * OUTPUT: The info in malloced space. To be freed by caller.
2N/A * NULL on failure.
2N/A */
2N/Astruct passwd_entry *
2N/Aget_old_info(char *name, char *domain)
2N/A{
2N/A const char *myself = "get_old_info";
2N/A struct passwd_entry *old_passwd;
2N/A char *p;
2N/A datum key, data;
2N/A suc_code res;
2N/A
2N/A /* Get the password entry */
2N/A key.dptr = name;
2N/A key.dsize = strlen(key.dptr);
2N/A read_from_dit(PASSWD_MAPPING, domain, &key, &data);
2N/A if (NULL == data.dptr) {
2N/A logmsg(MSG_NOTIMECHECK, LOG_ERR,
2N/A "Could not read old pwd for %s", name);
2N/A return (NULL);
2N/A }
2N/A
2N/A /* Pull password apart */
2N/A old_passwd = am(myself, sizeof (struct passwd_entry));
2N/A if (NULL == old_passwd) {
2N/A logmsg(MSG_NOMEM, LOG_ERR, "Could not alloc for pwd decode");
2N/A sfree(data.dptr);
2N/A return (NULL);
2N/A }
2N/A
2N/A /* No data yet */
2N/A old_passwd->pw_name = NULL;
2N/A old_passwd->pw_passwd = NULL;
2N/A old_passwd->pw_uid = NULL;
2N/A old_passwd->pw_gid = NULL;
2N/A old_passwd->pw_gecos = NULL;
2N/A old_passwd->pw_dir = NULL;
2N/A old_passwd->pw_shell = NULL;
2N/A old_passwd->adjunct_tail = NULL;
2N/A old_passwd->pwd_str = NULL;
2N/A old_passwd->adjunct_str = NULL;
2N/A old_passwd->adjunct = FALSE;
2N/A
2N/A res = decode_pwd_entry(&data, old_passwd, FALSE);
2N/A sfree(data.dptr);
2N/A if (SUCCESS != res) {
2N/A free_pwd_entry(old_passwd);
2N/A return (NULL);
2N/A }
2N/A
2N/A /* Try to get the adjunct entry */
2N/A read_from_dit(PASSWD_ADJUNCT_MAPPING, domain, &key, &data);
2N/A if (NULL == data.dptr) {
2N/A /* Fine just no adjunct data */
2N/A old_passwd->adjunct = FALSE;
2N/A } else {
2N/A res = decode_pwd_entry(&data, old_passwd, TRUE);
2N/A sfree(data.dptr);
2N/A if (SUCCESS != res) {
2N/A free_pwd_entry(old_passwd);
2N/A return (NULL);
2N/A }
2N/A }
2N/A
2N/A return (old_passwd);
2N/A}
2N/A
2N/A/*
2N/A * FUNCTION : put_new_info()
2N/A *
2N/A * DESCRIPTION: Generates new map strings and puts them back to LDAP
2N/A *
2N/A * INPUTS: Info to put back
2N/A * Domain
2N/A *
2N/A * OUTPUT: Answer code.
2N/A */
2N/Aint
2N/Aput_new_info(struct passwd_entry *pwd, char *domain)
2N/A{
2N/A datum key, data;
2N/A
2N/A /* Write it back to LDAP */
2N/A data.dptr = pwd->pwd_str;
2N/A data.dsize = strlen(data.dptr);
2N/A key.dptr = pwd->pw_name;
2N/A key.dsize = strlen(key.dptr);
2N/A if (SUCCESS != write_to_dit(PASSWD_MAPPING, domain, key, data,
2N/A TRUE, FALSE))
2N/A return (2);
2N/A
2N/A
2N/A /* If DIT contains adjunct information do the same for adjunct */
2N/A if (pwd->adjunct) {
2N/A data.dptr = pwd->adjunct_str;
2N/A data.dsize = strlen(data.dptr);
2N/A key.dptr = pwd->pw_name;
2N/A key.dsize = strlen(key.dptr);
2N/A if (SUCCESS != write_to_dit(PASSWD_ADJUNCT_MAPPING, domain,
2N/A key, data, TRUE, FALSE))
2N/A return (2);
2N/A }
2N/A
2N/A return (0);
2N/A
2N/A}
2N/A
2N/A/*
2N/A * FUNCTION : get_old_shadow()
2N/A *
2N/A * DESCRIPTION :Extracts and decodes shadow information from the DIT
2N/A * See also comments under decode_pwd_entry().
2N/A *
2N/A * INPUTS : User name
2N/A * Domain name
2N/A *
2N/A * OUTPUT : Shadow information in malloced memory. To be freed by caller.
2N/A */
2N/Astruct spwd *
2N/Aget_old_shadow(char *name, char *domain)
2N/A{
2N/A const char *myself = "get_old_shadow";
2N/A struct spwd *sp;
2N/A datum key, data;
2N/A suc_code res;
2N/A
2N/A /* Get the info */
2N/A key.dptr = name;
2N/A key.dsize = strlen(key.dptr); /* Len excluding terminator */
2N/A read_from_dit(AGEING_MAPPING, domain, &key, &data);
2N/A
2N/A if (NULL == data.dptr) {
2N/A /* OK just have no shadow info in DIT */
2N/A return (NULL);
2N/A }
2N/A
2N/A /* Pull shadow apart */
2N/A if (NULL == (sp = am(myself, sizeof (struct spwd)))) {
2N/A logmsg(MSG_NOMEM, LOG_ERR,
2N/A "Could not alloc for shadow decode");
2N/A sfree(data.dptr);
2N/A return (NULL);
2N/A }
2N/A sp->sp_namp = NULL;
2N/A sp->sp_pwdp = NULL;
2N/A
2N/A res = decode_shadow_entry(&data, sp);
2N/A sfree(data.dptr);
2N/A if (SUCCESS != res) {
2N/A free_shadow_entry(sp);
2N/A return (NULL);
2N/A }
2N/A
2N/A return (sp);
2N/A}
2N/A
2N/A/*
2N/A * FUNCTION : decode_shadow_entry()
2N/A *
2N/A * DESCRIPTION: Pulls apart ageing information. For convenience this is stored
2N/A * in a partially filled spwd structure.
2N/A *
2N/A * SEE COMMENTS FOR decode_pwd_entry()
2N/A */
2N/Asuc_code
2N/Adecode_shadow_entry(datum *data, struct spwd *sp)
2N/A{
2N/A char *dptr = data->dptr;
2N/A char *p, *str_end, *temp;
2N/A
2N/A /* Work out last location in string */
2N/A str_end = dptr + data->dsize;
2N/A
2N/A /* Name */
2N/A if (NULL == (p = get_next_token(dptr, &(sp->sp_namp), str_end)))
2N/A return (FAILURE);
2N/A
2N/A /* date of last change */
2N/A if (NULL == (p = get_next_token(p, &temp, str_end)))
2N/A return (FAILURE);
2N/A sp->sp_lstchg = atoi(temp);
2N/A
2N/A /* min days to passwd change */
2N/A if (NULL == (p = get_next_token(p, &temp, str_end)))
2N/A return (FAILURE);
2N/A sp->sp_min = atoi(temp);
2N/A
2N/A /* max days to passwd change */
2N/A if (NULL == (p = get_next_token(p, &temp, str_end)))
2N/A return (FAILURE);
2N/A sp->sp_max = atoi(temp);
2N/A
2N/A /* warning period */
2N/A if (NULL == (p = get_next_token(p, &temp, str_end)))
2N/A return (FAILURE);
2N/A sp->sp_warn = atoi(temp);
2N/A
2N/A /* max days inactive */
2N/A if (NULL == (p = get_next_token(p, &temp, str_end)))
2N/A return (FAILURE);
2N/A sp->sp_inact = atoi(temp);
2N/A
2N/A /* account expiry date */
2N/A if (NULL == (p = get_next_token(p, &temp, str_end)))
2N/A return (FAILURE);
2N/A sp->sp_expire = atoi(temp);
2N/A
2N/A /* flag */
2N/A if (NULL != (p = get_next_token(p, &temp, str_end)))
2N/A return (FAILURE);
2N/A sp->sp_flag = atoi(temp);
2N/A
2N/A return (SUCCESS);
2N/A}
2N/A
2N/A/*
2N/A * FUNCTION : write_shadow_info()
2N/A *
2N/A * DESCRIPTION: Writes shadow information back to the DIT.
2N/A *
2N/A * INPUTS : Domain
2N/A * Information to write
2N/A *
2N/A * OUTPUT : Success code
2N/A *
2N/A */
2N/Asuc_code
2N/Awrite_shadow_info(char *domain, struct spwd *sp)
2N/A{
2N/A const char *myself = "write_shadow_info";
2N/A datum key, data;
2N/A char *str;
2N/A suc_code res;
2N/A int len;
2N/A
2N/A /* Work out how long string will be */
2N/A len = strlen(sp->sp_namp) + 1;
2N/A
2N/A /*
2N/A * Bit crude but if we assume 1 byte is 3 decimal characters
2N/A * will get enough buffer for the longs and some spare.
2N/A */
2N/A len += 7 * (3 * sizeof (long) + 1);
2N/A
2N/A /* Allocate some memory */
2N/A str = am(myself, len);
2N/A if (NULL == str) {
2N/A logmsg(MSG_NOMEM, LOG_ERR, "Could not aloc for shadow write");
2N/A return (FAILURE);
2N/A }
2N/A
2N/A /* Build up shadow string */
2N/A sprintf(str, "%s%c%d%c%d%c%d%c%d%c%d%c%d%c%d",
2N/A sp->sp_namp, COLON_CHAR,
2N/A sp->sp_lstchg, COLON_CHAR,
2N/A sp->sp_min, COLON_CHAR,
2N/A sp->sp_max, COLON_CHAR,
2N/A sp->sp_warn, COLON_CHAR,
2N/A sp->sp_inact, COLON_CHAR,
2N/A sp->sp_expire, COLON_CHAR,
2N/A sp->sp_flag);
2N/A
2N/A /* Write it */
2N/A data.dptr = str;
2N/A data.dsize = strlen(data.dptr);
2N/A key.dptr = sp->sp_namp;
2N/A key.dsize = strlen(key.dptr);
2N/A res = write_to_dit(AGEING_MAPPING, domain, key, data, TRUE, FALSE);
2N/A
2N/A sfree(str);
2N/A return (res);
2N/A}
2N/A
2N/A/*
2N/A * FUNCTION : free_shadow_entry()
2N/A *
2N/A * DESCRIPTION: Frees up a shadow information structure
2N/A *
2N/A * INPUTS : Structure to free
2N/A *
2N/A * OUTPUTS : Nothing
2N/A */
2N/Avoid
2N/Afree_shadow_entry(struct spwd *spwd)
2N/A{
2N/A if (NULL != spwd->sp_namp)
2N/A sfree(spwd->sp_namp);
2N/A
2N/A if (NULL != spwd->sp_pwdp)
2N/A sfree(spwd->sp_pwdp);
2N/A
2N/A /* No need to free numerics */
2N/A
2N/A /* Free up structure */
2N/A sfree(spwd);
2N/A}