shim_changepasswd.c revision 2
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 * See the License for the specific language governing permissions 2N/A * and limitations under the License. 2N/A * When distributing Covered Code, include this CDDL HEADER in each 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 * Copyright (c) 2003, 2011, Oracle and/or its affiliates. All rights reserved. 2N/A * version modifies the NIS source files and then initiates a 2N/A * ypmake to make the maps and push them. 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 * 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 * What was previously in the maps is irrelevant. 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/* DO NOT INCLUDE SHIM_HOOKS.H */ 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/* Names of password and adjunct mappings. Used to access DIT */ 2N/A/* Bitmasks used in list of fields to change */ 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 * 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 * FUNCTION: shim_changepasswd() 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 * INPUTS: Identical to changepasswd() 2N/A * OUTPUTS: Identical to changepasswd() 2N/A /* Clean out yppwd ... maybe we don't trust RPC */ 2N/A /* Get the RPC args */ 2N/A /* Perform basic validation */ 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 "Couldn't get local user credentials");
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 /* Got a domain list ... process all the domains */ 2N/A /* If one has worked don't care if others fail */ 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 "Could not get any domain info");
2N/A /* Got one domain ... process it. */ 2N/A /* Send reply packet */ 2N/A "could not reply to RPC call");
2N/A * FUNCTION : proc_domain() 2N/A * DESCRIPTION: Process a request for one domain 2N/A * GIVEN : Pointer to the request. 2N/A * Root on master flag 2N/A * OUTPUTS : Answer code for reply 2N/A /* security hole fix from original source */ 2N/A *p =
'$';
/* you lose buckwheat */ 2N/A *p =
'$';
/* you lose buckwheat */ 2N/A /* Get old info from DIT for this domain */ 2N/A "Could not get old information for %s in " 2N/A /* Have a request that can be replied to */ 2N/A * FUNCTION : proc_request() 2N/A * DESCRIPTION: Process a request 2N/A * GIVEN : Pointer to the request. 2N/A * Pointer to old information from LDAP 2N/A * Root on master flag 2N/A * OUTPUTS : Answer code for reply 2N/A /* Work out what we have to change and change it */ 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 "Could not create passwd entry");
2N/A "Could not create adjunct entry");
2N/A /* Put the information back to DIT */ 2N/A /* Are going to be forking pushes, set up signals */ 2N/A /* Update and push all the maps */ 2N/A /* Tidy up signals */ 2N/A * FUNCTION: proc_maps() 2N/A * DESCRIPTION: Gets all the map lists and processes them. 2N/A * INPUTS: Domain name 2N/A * New info to write into maps 2N/A * OUTPUT : Answer code 2N/A /* Get list of passwd maps from mapping file */ 2N/A /* Process list of passwd maps */ 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 /* Get list of adjunct maps from mapping file */ 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 * FUNCTION: proc_map_list() 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 * INPUTS: Null terminated list of maps to process. 2N/A * Information to write (including user name) 2N/A * Flag indicating if this is the adjunct list 2N/A * OUTPUTS: An error code 2N/A /* If this is a adjunct list check LDAP had some adjunct info */ 2N/A "Have adjunct map list but no adjunct data in DIT");
2N/A /* Not a disaster */ 2N/A /* Allocate enough buffer to take longest map name */ 2N/A /* For all maps in list */ 2N/A /* Generate full map name */ 2N/A /* Done with full map path */ 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 * 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 * Define full path to yppush. Probably also 2N/A * best for security. 2N/A "Could not initiate yppush");
2N/A * FUNCTION : update_single_map() 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 * versions of dbm_functions explicitly where needed. 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 * OUTPUT : Answer code 2N/A /* Set up key dependent on which type of map this is */ 2N/A return (0);
/* Next map */ 2N/A return (0);
/* Next map */ 2N/A /* Lock map for update. Painful and may block but have to do it */ 2N/A /* Do the update use simple DBM operation */ 2N/A /* update entry TTL. If we fail not a problem will just timeout early */ 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 "YP_LAST_MODIFIED %s will not be pushed this time",
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 * FUNCTION : strend() 2N/A * DESCRIPTION: Determines if one string ends with another. 2N/A * FUNCTION: modify_ent() 2N/A * DESCRIPTION: Modify an entry to reflect a request. 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 * OUTPUT: Error code 2N/A /* Get list of changes */ 2N/A /* Check that the shell we have been given is acceptable. */ 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 /* Try to get shadow information */ 2N/A /* If there is shadow information make password aging checks */ 2N/A /* password aging - bug for bug compatibility */ 2N/A "Sorry: < %ld days since " 2N/A /* Update time of change */ 2N/A /* Make changes to old entity */ 2N/A * FUNCTION : get_change_list() 2N/A * DESCRIPTION: Works out what we have to change. 2N/A * Structure containing current state of entry 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 !(*p++ ==
'#' && *p++ ==
'#' &&
2N/A * FUNCTION : decode_pwd_entry() 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 * Substrings are put into malloced memory. Caller to free. 2N/A * For adjunct files most of it is left empty. 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 * 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 * OUTPUTS: SUCCESS = Decoded successfully 2N/A * FAILURE = Not decoded successfully. Caller to tidy up. 2N/A /* Work out last location in string */ 2N/A /* If we found an adjunct version this is the one to use */ 2N/A /* If we found an adjunct version this is the one to use */ 2N/A /* Store adjunct information in opaque string */ 2N/A /* Remember that LDAP contained adjunct data */ 2N/A /* If we get here not adjunct. Decode rest of passwd */ 2N/A /* Shell may not be present so don't check return */ 2N/A * FUNCTION : get_next_token() 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 * 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 * OUTPUT : Pointer into the string immediately after the token. 2N/A * NULL if end of string reached or error. 2N/A /* Skip leading whitespace */ 2N/A /* Required string is now between start and end */ 2N/A "Could not alloc memory for token");
2N/A /* Terminate token */ 2N/A /* Check if we reached the end of the input string */ 2N/A /* There is some more */ 2N/A * FUNCTION : free_pwd_entry() 2N/A * DESCRIPTION: Frees up a pwd_entry structure and its contents. 2N/A * INPUTS: Pointer to the structure to free. 2N/A /* Free up strings */ 2N/A /* Free up structure */ 2N/A * FUNCTION : create_pwd_str() 2N/A * DESCRIPTION: Builds up a new password entity string from a passwd structure. 2N/A * INPUTS : Structure containing password details 2N/A * Flag indicating if we should create an adjunct or passwd string. 2N/A * OUTPUTS : String in malloced memory (to be freed by caller). 2N/A /* Separator string so we can strcat separator onto things */ 2N/A /* Work out the size */ 2N/A /* Allocate some memory for it */ 2N/A /* Build up a passwd string */ 2N/A /* If LDAP contains adjunct info then passwd is 'x' */ 2N/A /* Build up a passwd_adjunct string */ 2N/A * FUNCTION: get_old_info() 2N/A * DESCRIPTION: Gets as much information as possible from LDAP about one user. 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 * INPUT: Pointer to user name 2N/A * OUTPUT: The info in malloced space. To be freed by caller. 2N/A /* Get the password entry */ 2N/A "Could not read old pwd for %s",
name);
2N/A /* Pull password apart */ 2N/A /* Try to get the adjunct entry */ 2N/A /* Fine just no adjunct data */ 2N/A * FUNCTION : put_new_info() 2N/A * DESCRIPTION: Generates new map strings and puts them back to LDAP 2N/A * INPUTS: Info to put back 2N/A * OUTPUT: Answer code. 2N/A /* Write it back to LDAP */ 2N/A /* If DIT contains adjunct information do the same for adjunct */ 2N/A * FUNCTION : get_old_shadow() 2N/A * DESCRIPTION :Extracts and decodes shadow information from the DIT 2N/A * See also comments under decode_pwd_entry(). 2N/A * INPUTS : User name 2N/A * OUTPUT : Shadow information in malloced memory. To be freed by caller. 2N/A /* OK just have no shadow info in DIT */ 2N/A /* Pull shadow apart */ 2N/A "Could not alloc for shadow decode");
2N/A * FUNCTION : decode_shadow_entry() 2N/A * DESCRIPTION: Pulls apart ageing information. For convenience this is stored 2N/A * in a partially filled spwd structure. 2N/A * SEE COMMENTS FOR decode_pwd_entry() 2N/A /* Work out last location in string */ 2N/A /* date of last change */ 2N/A /* min days to passwd change */ 2N/A /* max days to passwd change */ 2N/A /* warning period */ 2N/A /* max days inactive */ 2N/A /* account expiry date */ 2N/A * FUNCTION : write_shadow_info() 2N/A * DESCRIPTION: Writes shadow information back to the DIT. 2N/A * Information to write 2N/A * OUTPUT : Success code 2N/A /* Work out how long string will be */ 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 len +=
7 * (
3 *
sizeof (
long) +
1);
2N/A /* Allocate some memory */ 2N/A /* Build up shadow string */ 2N/A * FUNCTION : free_shadow_entry() 2N/A * DESCRIPTION: Frees up a shadow information structure 2N/A * INPUTS : Structure to free 2N/A /* No need to free numerics */ 2N/A /* Free up structure */