4 * The contents of this file are subject to the terms of the
5 * Common Development and Distribution License (the "License").
6 * You may not use this file except in compliance with the License.
8 * You can obtain a copy of the license at usr/src/OPENSOLARIS.LICENSE
9 * or http://www.opensolaris.org/os/licensing.
10 * See the License for the specific language governing permissions
11 * and limitations under the License.
13 * When distributing Covered Code, include this CDDL HEADER in each
14 * file and include the License file at usr/src/OPENSOLARIS.LICENSE.
15 * If applicable, add the following below this CDDL HEADER, with the
16 * fields enclosed by brackets "[]" replaced with your own identifying
17 * information: Portions Copyright [yyyy] [name of copyright owner]
22 * Copyright 2015 Gary Mills
23 * Copyright 2006 Sun Microsystems, Inc. All rights reserved.
24 * Use is subject to license terms.
28 * DESCRIPTION: This is the N2L equivalent of changepasswd.c. The traditional
29 * version modifies the NIS source files and then initiates a
30 * ypmake to make the maps and push them.
32 * For N2L there are no source files and the policy is that the
33 * definitive information is that contained in the DIT. Old
34 * information is read from LDAP. Assuming this authenticates, and
35 * the change is acceptable, this information is modified and
36 * written back to LDAP.
38 * Related map entries are then found and updated finally
39 * yppushes of the changed maps are initiated. Since the
40 * definitive information has already correctly been updated the
41 * code is tolerant of some errors during this operation.
43 * What was previously in the maps is irrelevant.
45 * Some less than perfect code (like inline constants for
46 * return values and a few globals) is retained from the original.
49 #include <sys/types.h>
62 #include <rpcsvc/yppasswd.h>
67 /* DO NOT INCLUDE SHIM_HOOKS.H */
70 #include "../ldap_util.h"
73 * Undocumented external function in libnsl
75 extern int getdomainname(char *, int);
78 #define CRYPTPWSIZE CRYPT_MAXCIPHERTEXTLEN
80 #define FINGERSIZE (4 * STRSIZE - 4)
81 #define SHELLSIZE (STRSIZE - 2)
83 #define UTUSERLEN (sizeof (((struct utmpx *)0)->ut_user))
84 #define COLON_CHAR ':'
87 * Path to DBM files. This is only required for N2L mode. Traditional mode
88 * works with the source files and uses the NIS Makefile to generate the maps.
89 * Seems to be hard coded in the rest of NIS so same is done here.
91 #define YPDBPATH "/var/yp"
93 /* Names of password and adjunct mappings. Used to access DIT */
94 #define BYNAME ".byname"
95 #define BYUID ".byuid"
96 #define BYGID ".bygid"
97 #define PASSWD_MAPPING "passwd" BYNAME
98 #define PASSWD_ADJUNCT_MAPPING "passwd.adjunct" BYNAME
99 #define AGEING_MAPPING "ageing" BYNAME
101 /* Bitmasks used in list of fields to change */
102 #define CNG_PASSWD 0x0001
103 #define CNG_SH 0x0002
104 #define CNG_GECOS 0x0004
107 extern int single
, nogecos
, noshell
, nopw
, mflag
;
110 * Structure for containing the information is currently in the DIT. This is
111 * similar to the passwd structure defined in getpwent(3C) apart from.
113 * 1. Since GID and UID are never changed they are not converted to integers.
114 * 2. There are extra fields to hold adjunct information.
115 * 3. There are extra fields to hold widely used information.
117 struct passwd_entry
{
125 char *adjunct_tail
; /* Tail of adjunct entry (opaque) */
126 bool_t adjunct
; /* Flag indicating if DIT has adjunct info */
127 char *pwd_str
; /* New password string */
128 char *adjunct_str
; /* New adjunct string */
132 extern bool_t
validloginshell(char *sh
, char *arg
, int);
133 extern int validstr(char *str
, size_t size
);
135 suc_code
write_shadow_info(char *, struct spwd
*);
136 int put_new_info(struct passwd_entry
*, char *);
137 char *create_pwd_str(struct passwd_entry
*, bool_t
);
138 int proc_domain(struct yppasswd
*, bool_t
, char *);
139 int proc_request(struct yppasswd
*, struct passwd_entry
*, bool_t
, char *);
140 int modify_ent(struct yppasswd
*, struct passwd_entry
*t
, bool_t
, char *);
141 int get_change_list(struct yppasswd
*, struct passwd_entry
*);
142 struct passwd_entry
*get_old_info(char *, char *);
143 static char *get_next_token(char *, char **, char *);
144 void free_pwd_entry(struct passwd_entry
*);
145 struct spwd
*get_old_shadow(char *, char *);
146 suc_code
decode_shadow_entry(datum
*, struct spwd
*);
147 void free_shadow_entry(struct spwd
*);
148 int proc_maps(char *, struct passwd_entry
*);
149 int proc_map_list(char **, char *, struct passwd_entry
*, bool_t
);
150 int update_single_map(char *, struct passwd_entry
*, bool_t
);
151 bool_t
strend(char *s1
, char *s2
);
154 * FUNCTION: shim_changepasswd()
156 * DESCRIPTION: N2L version of changepasswd(). When this is called 'useshadow'
157 * etc. will have been set up but are meaningless. We work out
158 * what to change based on information from the DIT.
160 * INPUTS: Identical to changepasswd()
162 * OUTPUTS: Identical to changepasswd()
165 shim_changepasswd(SVCXPRT
*transp
)
167 struct yppasswd yppwd
;
168 bool_t root_on_master
= FALSE
;
169 char domain
[MAXNETNAMELEN
+1];
173 int ret
, ans
= 2; /* Answer codes */
175 /* Clean out yppwd ... maybe we don't trust RPC */
176 memset(&yppwd
, 0, sizeof (struct yppasswd
));
178 /* Get the RPC args */
179 if (!svc_getargs(transp
, xdr_yppasswd
, (caddr_t
)&yppwd
)) {
180 svcerr_decode(transp
);
184 /* Perform basic validation */
185 if ((!validstr(yppwd
.newpw
.pw_passwd
, CRYPTPWSIZE
)) ||
186 (!validstr(yppwd
.newpw
.pw_name
, UTUSERLEN
)) ||
187 (!validstr(yppwd
.newpw
.pw_gecos
, FINGERSIZE
)) ||
188 (!validstr(yppwd
.newpw
.pw_shell
, SHELLSIZE
))) {
189 svcerr_decode(transp
);
194 * Special case: root on the master server can change other
195 * users' passwords without first entering the old password.
196 * We need to ensure that this is indeed root on the master
197 * server. (bug 1253949)
199 if (strcmp(transp
->xp_netid
, "ticlts") == 0) {
200 svc_local_cred_t cred
;
201 if (!svc_get_local_cred(transp
, &cred
)) {
202 logmsg(MSG_NOTIMECHECK
, LOG_ERR
,
203 "Couldn't get local user credentials");
204 } else if (cred
.ruid
== 0)
205 root_on_master
= TRUE
;
209 * Get the domain name. This is tricky because a N2L server may be
210 * handling multiple domains. There is nothing in the request to
211 * indicate which one we are trying to change a passwd for. First
212 * we try to get a list of password related domains from the mapping
216 (dom_count
= get_mapping_yppasswdd_domain_list(&domain_list
))) {
217 /* Got a domain list ... process all the domains */
218 for (i
= 0; i
< dom_count
; i
++) {
219 ret
= proc_domain(&yppwd
, root_on_master
,
222 /* If one has worked don't care if others fail */
230 * There was no domain list in the mapping file. The
231 * traditional version of this code calls ypmake which picks
232 * up the domain returned by getdomainname(). Fall back to the
235 if (0 > getdomainname(domain
, MAXNETNAMELEN
+1)) {
236 logmsg(MSG_NOTIMECHECK
, LOG_ERR
,
237 "Could not get any domain info");
239 /* Got one domain ... process it. */
240 ans
= proc_domain(&yppwd
, root_on_master
, domain
);
244 /* Send reply packet */
245 if (!svc_sendreply(transp
, xdr_int
, (char *)&ans
))
246 logmsg(MSG_NOTIMECHECK
, LOG_WARNING
,
247 "could not reply to RPC call");
251 * FUNCTION : proc_domain()
253 * DESCRIPTION: Process a request for one domain
255 * GIVEN : Pointer to the request.
256 * Root on master flag
259 * OUTPUTS : Answer code for reply
262 proc_domain(struct yppasswd
*yppwd
, bool_t root_on_master
, char *domain
)
264 struct passwd_entry
*old_pwd
;
268 /* security hole fix from original source */
269 for (p
= yppwd
->newpw
.pw_name
; (*p
!= '\0'); p
++)
270 if ((*p
== ':') || !(isprint(*p
)))
271 *p
= '$'; /* you lose buckwheat */
272 for (p
= yppwd
->newpw
.pw_passwd
; (*p
!= '\0'); p
++)
273 if ((*p
== ':') || !(isprint(*p
)))
274 *p
= '$'; /* you lose buckwheat */
276 /* Get old info from DIT for this domain */
277 old_pwd
= get_old_info(yppwd
->newpw
.pw_name
, domain
);
278 if (NULL
== old_pwd
) {
279 logmsg(MSG_NOTIMECHECK
, LOG_ERR
,
280 "Could not get old information for %s in "
281 "domain %s", yppwd
->newpw
.pw_name
, domain
);
285 /* Have a request that can be replied to */
286 ans
= proc_request(yppwd
, old_pwd
, root_on_master
, domain
);
287 free_pwd_entry(old_pwd
);
293 * FUNCTION : proc_request()
295 * DESCRIPTION: Process a request
297 * GIVEN : Pointer to the request.
298 * Pointer to old information from LDAP
299 * Root on master flag
302 * OUTPUTS : Answer code for reply
305 proc_request(struct yppasswd
*yppwd
, struct passwd_entry
*old_pwd
,
306 bool_t root_on_master
, char *domain
)
308 struct sigaction sa
, osa1
, osa2
, osa3
;
312 if ((0 != strcmp(crypt(yppwd
->oldpass
, old_pwd
->pw_passwd
),
313 old_pwd
->pw_passwd
)) && !root_on_master
) {
314 logmsg(MSG_NOTIMECHECK
, LOG_NOTICE
, "Passwd incorrect %s",
315 yppwd
->newpw
.pw_name
);
319 /* Work out what we have to change and change it */
320 ans
= modify_ent(yppwd
, old_pwd
, root_on_master
, domain
);
325 * Generate passwd and adjunct map entries. This creates extra
326 * malloced strings in old_pwd. These will be freed when
327 * free_pwd_entry() is called to free up the rest of the structure.
329 old_pwd
->pwd_str
= create_pwd_str(old_pwd
, FALSE
);
330 if (NULL
== old_pwd
->pwd_str
) {
331 logmsg(MSG_NOTIMECHECK
, LOG_ERR
,
332 "Could not create passwd entry");
335 if (old_pwd
->adjunct
) {
336 old_pwd
->adjunct_str
= create_pwd_str(old_pwd
, TRUE
);
337 if (NULL
== old_pwd
->adjunct_str
) {
338 logmsg(MSG_NOTIMECHECK
, LOG_ERR
,
339 "Could not create adjunct entry");
343 old_pwd
->adjunct_str
= NULL
;
346 /* Put the information back to DIT */
347 ans
= put_new_info(old_pwd
, domain
);
352 /* Are going to be forking pushes, set up signals */
353 memset(&sa
, 0, sizeof (struct sigaction
));
354 sa
.sa_handler
= SIG_IGN
;
355 sigaction(SIGTSTP
, &sa
, (struct sigaction
*)0);
356 sigaction(SIGHUP
, &sa
, &osa1
);
357 sigaction(SIGINT
, &sa
, &osa2
);
358 sigaction(SIGQUIT
, &sa
, &osa3
);
360 /* Update and push all the maps */
361 ans
= proc_maps(domain
, old_pwd
);
363 /* Tidy up signals */
364 sigaction(SIGHUP
, &osa1
, (struct sigaction
*)0);
365 sigaction(SIGINT
, &osa2
, (struct sigaction
*)0);
366 sigaction(SIGQUIT
, &osa3
, (struct sigaction
*)0);
372 * FUNCTION: proc_maps()
374 * DESCRIPTION: Gets all the map lists and processes them.
376 * INPUTS: Domain name
377 * New info to write into maps
379 * OUTPUT : Answer code
382 proc_maps(char *domain
, struct passwd_entry
*pwd
)
384 char **map_list
; /* Array of passwd or adjunct maps */
387 /* Get list of passwd maps from mapping file */
388 map_list
= get_passwd_list(FALSE
, domain
);
389 if (map_list
!= NULL
) {
390 /* Process list of passwd maps */
391 ans
= proc_map_list(map_list
, domain
, pwd
, FALSE
);
392 free_passwd_list(map_list
);
398 * If we get here either there were no passwd maps or there were
399 * some and they were processed successfully. Either case is good
400 * continue and process passwd.adjunct maps.
403 /* Get list of adjunct maps from mapping file */
404 map_list
= get_passwd_list(TRUE
, domain
);
405 if (map_list
!= NULL
) {
407 * Process list of adjunct maps. If the required information
408 * is not present in LDAP then the updates attempts will log
409 * an error. No need to make the check here
411 ans
= proc_map_list(map_list
, domain
, pwd
, TRUE
);
412 free_passwd_list(map_list
);
419 * FUNCTION: proc_map_list()
421 * DESCRIPTION: Finds entries in one list of map that need to be updated.
422 * updates them and writes them back.
424 * INPUTS: Null terminated list of maps to process.
426 * Information to write (including user name)
427 * Flag indicating if this is the adjunct list
429 * OUTPUTS: An error code
432 proc_map_list(char **map_list
, char *domain
,
433 struct passwd_entry
*pwd
, bool_t adjunct_flag
)
435 char *myself
= "proc_map_list";
438 int map_name_len
= 0;
441 /* If this is a adjunct list check LDAP had some adjunct info */
442 if ((adjunct_flag
) && (!pwd
->adjunct
)) {
443 logmsg(MSG_NOTIMECHECK
, LOG_INFO
,
444 "Have adjunct map list but no adjunct data in DIT");
449 /* Allocate enough buffer to take longest map name */
450 for (index
= 0; map_list
[index
] != NULL
; index
++)
451 if (map_name_len
< strlen(map_list
[index
]))
452 map_name_len
= strlen(map_list
[index
]);
453 map_name_len
+= strlen(YPDBPATH
);
454 map_name_len
+= strlen(NTOL_PREFIX
);
455 map_name_len
+= strlen(domain
);
457 if (NULL
== (map_name
= am(myself
, map_name_len
))) {
458 logmsg(MSG_NOMEM
, LOG_ERR
, "Could not alloc map name");
462 /* For all maps in list */
463 for (index
= 0; map_list
[index
] != NULL
; index
++) {
465 /* Generate full map name */
466 strcpy(map_name
, YPDBPATH
);
467 add_separator(map_name
);
468 strcat(map_name
, domain
);
469 add_separator(map_name
);
470 strcat(map_name
, NTOL_PREFIX
);
471 strcat(map_name
, map_list
[index
]);
473 if (0 != (ans
= update_single_map(map_name
, pwd
, adjunct_flag
)))
477 /* Done with full map path */
481 * If (ans != 0) then one more maps have failed. LDAP has however been
482 * updates. This is the definitive source for information there is no
483 * need to unwind. (This was probably due to maps that were already
488 * If it all worked fork off push operations for the maps. Since we
489 * want the map to end up with it's traditional name on the slave send
490 * the name without its LDAP_ prefix. The slave will call ypxfrd
491 * which, since it is running in N2L mode, will put the prefix back on
492 * before reading the file.
494 if (mflag
&& (0 == ans
)) {
495 for (index
= 0; (map_name
= map_list
[index
]) != NULL
;
499 * Define full path to yppush. Probably also
502 strcpy(cmdbuf
, "/usr/lib/netsvc/yp/yppush ");
503 strcat(cmdbuf
, map_name
);
504 if (0 > system(cmdbuf
))
505 logmsg(MSG_NOTIMECHECK
, LOG_ERR
,
506 "Could not initiate yppush");
515 * FUNCTION : update_single_map()
517 * DESCRIPTION: Updates one map. This is messy because we want to lock the map
518 * to prevent other processes from updating it at the same time.
519 * This mandates that we open it using the shim. When we
520 * write to it however we DO NOT want to write through to LDAP
521 * i.e. do not want to use the shim.
523 * Solution : Do not include shim_hooks.h but call the shim
524 * versions of dbm_functions explicitly where needed.
526 * INPUT : Full name of map
527 * Information to write (including user name)
528 * Flag indicating if this is an adjunct map.
530 * OUTPUT : Answer code
534 update_single_map(char *map_name
, struct passwd_entry
*pwd
, bool_t adjunct_flag
)
542 data
.dptr
= pwd
->adjunct_str
;
544 data
.dptr
= pwd
->pwd_str
;
545 data
.dsize
= strlen(data
.dptr
);
547 /* Set up key dependent on which type of map this is */
549 if (strend(map_name
, BYNAME
))
550 key
.dptr
= pwd
->pw_name
;
551 if (strend(map_name
, BYUID
))
552 key
.dptr
= pwd
->pw_uid
;
553 if (strend(map_name
, BYGID
))
554 key
.dptr
= pwd
->pw_gid
;
556 if (NULL
== key
.dptr
) {
557 logmsg(MSG_NOTIMECHECK
, LOG_ERR
,
558 "Unrecognized map type %s", map_name
);
559 return (0); /* Next map */
561 key
.dsize
= strlen(key
.dptr
);
564 map
= shim_dbm_open(map_name
, O_RDWR
, 0600);
566 logmsg(MSG_NOTIMECHECK
, LOG_ERR
, "Could not open %s", map_name
);
567 return (0); /* Next map */
570 /* Lock map for update. Painful and may block but have to do it */
571 if (SUCCESS
!= lock_map_update((map_ctrl
*)map
)) {
572 logmsg(MSG_NOTIMECHECK
, LOG_ERR
,
573 "Could not lock map %s for update", map_name
);
578 /* Do the update use simple DBM operation */
579 res
= dbm_store(((map_ctrl
*)map
)->entries
, key
, data
, DBM_REPLACE
);
581 /* update entry TTL. If we fail not a problem will just timeout early */
582 update_entry_ttl((map_ctrl
*)map
, &key
, TTL_RAND
);
585 * Map has been modified so update YP_LAST_MODIFIED. In the vanilla
586 * NIS case this would have been done by the ypmake done after updating
587 * the passwd source file. If this fails not a great problem the map
589 if (FAILURE
== update_timestamp(((map_ctrl
*)map
)->entries
)) {
590 logmsg(MSG_NOTIMECHECK
, LOG_ERR
, "Could not update "
591 "YP_LAST_MODIFIED %s will not be pushed this time",
596 * Possibly should hold the lock until after push is complete
597 * but this could deadlock if client is slow and ypxfrd also
598 * decides to do an update.
600 unlock_map_update((map_ctrl
*)map
);
606 logmsg(MSG_NOTIMECHECK
, LOG_ERR
,
607 "Could not update map %s", map_name
);
615 * FUNCTION : strend()
617 * DESCRIPTION: Determines if one string ends with another.
620 strend(char *s1
, char *s2
)
624 len_dif
= strlen(s1
) - strlen(s2
);
627 if (0 == strcmp(s1
+ len_dif
, s2
))
633 * FUNCTION: modify_ent()
635 * DESCRIPTION: Modify an entry to reflect a request.
637 * INPUT: Pointer to the request.
638 * Pointer to the entry to modify.
639 * Flag indication if we are root on master
645 modify_ent(struct yppasswd
*yppwd
, struct passwd_entry
*old_ent
,
646 bool_t root_on_master
, char *domain
)
652 /* Get list of changes */
653 change_list
= get_change_list(yppwd
, old_ent
);
656 logmsg(MSG_NOTIMECHECK
, LOG_NOTICE
,
657 "No change for %s", yppwd
->newpw
.pw_name
);
661 /* Check that the shell we have been given is acceptable. */
662 if ((change_list
& CNG_SH
) && (!validloginshell(old_ent
->pw_shell
,
663 yppwd
->newpw
.pw_shell
, root_on_master
)))
667 * If changing the password do any aging checks.
668 * Since there are no shadow maps this is done by accessing
669 * attributes in the DIT via the mapping system.
671 if (change_list
& CNG_PASSWD
) {
673 /* Try to get shadow information */
674 shadow
= get_old_shadow(yppwd
->newpw
.pw_name
, domain
);
676 /* If there is shadow information make password aging checks */
677 if (NULL
!= shadow
) {
679 /* password aging - bug for bug compatibility */
680 if (shadow
->sp_max
!= -1) {
681 if (now
< shadow
->sp_lstchg
+ shadow
->sp_min
) {
682 logmsg(MSG_NOTIMECHECK
, LOG_ERR
,
683 "Sorry: < %ld days since "
684 "the last change", shadow
->sp_min
);
685 free_shadow_entry(shadow
);
690 /* Update time of change */
691 shadow
->sp_lstchg
= now
;
694 write_shadow_info(domain
, shadow
);
696 free_shadow_entry(shadow
);
700 /* Make changes to old entity */
701 if (change_list
& CNG_GECOS
) {
702 if (NULL
!= old_ent
->pw_gecos
)
703 sfree(old_ent
->pw_gecos
);
704 old_ent
->pw_gecos
= strdup(yppwd
->newpw
.pw_gecos
);
705 if (NULL
== old_ent
->pw_gecos
) {
706 logmsg(MSG_NOMEM
, LOG_ERR
, "Could not allocate gecos");
711 if (change_list
& CNG_SH
) {
712 if (NULL
!= old_ent
->pw_shell
)
713 sfree(old_ent
->pw_shell
);
714 old_ent
->pw_shell
= strdup(yppwd
->newpw
.pw_shell
);
715 if (NULL
== old_ent
->pw_shell
) {
716 logmsg(MSG_NOMEM
, LOG_ERR
, "Could not allocate shell");
721 if (change_list
& CNG_PASSWD
) {
722 if (NULL
!= old_ent
->pw_passwd
)
723 sfree(old_ent
->pw_passwd
);
724 old_ent
->pw_passwd
= strdup(yppwd
->newpw
.pw_passwd
);
725 if (NULL
== old_ent
->pw_passwd
) {
726 logmsg(MSG_NOMEM
, LOG_ERR
, "Could not allocate passwd");
735 * FUNCTION : get_change_list()
737 * DESCRIPTION: Works out what we have to change.
740 * Structure containing current state of entry
742 * OUTPUTS : A bitmask signaling what to change. (Implemented in this
743 * way to make it easy to pass between functions).
746 get_change_list(struct yppasswd
*yppwd
, struct passwd_entry
*old_ent
)
751 p
= yppwd
->newpw
.pw_passwd
;
754 !(*p
++ == '#' && *p
++ == '#' &&
755 (strcmp(p
, old_ent
->pw_name
) == 0)) &&
756 (strcmp(crypt(old_ent
->pw_passwd
,
757 yppwd
->newpw
.pw_passwd
), yppwd
->newpw
.pw_passwd
) != 0))
760 if ((NULL
!= old_ent
->pw_shell
) &&
762 (strcmp(old_ent
->pw_shell
, yppwd
->newpw
.pw_shell
) != 0)) {
768 if ((NULL
!= old_ent
->pw_gecos
) &&
770 (strcmp(old_ent
->pw_gecos
, yppwd
->newpw
.pw_gecos
) != 0)) {
780 * FUNCTION : decode_pwd_entry()
782 * DESCRIPTION: Pulls apart a password entry. Because the password entry has
783 * come from the mapping system it can be assumed to be correctly
784 * formatted and relatively simple parsing can be done.
786 * Substrings are put into malloced memory. Caller to free.
788 * For adjunct files most of it is left empty.
790 * It would be nice to use getpwent and friends for this work but
791 * these only seem to exist for files and it seems excessive to
792 * create a temporary file for this operation.
794 * INPUTS: Pointer to datum containing password string.
795 * Pointer to structure in which to return results
796 * Flag indicating if we are decoding passwd or passwd.adjunct
798 * OUTPUTS: SUCCESS = Decoded successfully
799 * FAILURE = Not decoded successfully. Caller to tidy up.
802 decode_pwd_entry(datum
*data
, struct passwd_entry
*pwd
, bool_t adjunct
)
804 char *myself
= "decode_pwd_entry";
805 char *p
, *str_end
, *temp
;
807 /* Work out last location in string */
808 str_end
= data
->dptr
+ data
->dsize
;
811 if (NULL
== (p
= get_next_token(data
->dptr
, &temp
, str_end
)))
814 /* If we found an adjunct version this is the one to use */
815 if (NULL
!= pwd
->pw_name
)
821 if (NULL
== (p
= get_next_token(p
, &temp
, str_end
)))
824 /* If we found an adjunct version this is the one to use */
825 if (NULL
!= pwd
->pw_passwd
)
826 sfree(pwd
->pw_passwd
);
828 pwd
->pw_passwd
= temp
;
831 /* Store adjunct information in opaque string */
832 pwd
->adjunct_tail
= am(myself
, str_end
- p
+ 1);
833 if (NULL
== pwd
->adjunct_tail
)
835 strncpy(pwd
->adjunct_tail
, p
, str_end
- p
);
836 pwd
->adjunct_tail
[str_end
- p
] = '\0';
838 /* Remember that LDAP contained adjunct data */
843 /* If we get here not adjunct. Decode rest of passwd */
846 if (NULL
== (p
= get_next_token(p
, &(pwd
->pw_uid
), str_end
)))
850 if (NULL
== (p
= get_next_token(p
, &(pwd
->pw_gid
), str_end
)))
854 if (NULL
== (p
= get_next_token(p
, &(pwd
->pw_gecos
), str_end
)))
858 if (NULL
== (p
= get_next_token(p
, &(pwd
->pw_dir
), str_end
)))
861 /* Shell may not be present so don't check return */
862 get_next_token(p
, &(pwd
->pw_shell
), str_end
);
864 if (NULL
== pwd
->pw_shell
)
871 * FUNCTION : get_next_token()
873 * DESCRIPTION: Gets the next token from a string upto the next colon or the
874 * end of the string. The duplicates this token into malloced
875 * memory removing any spaces.
877 * INPUTS : String to search for token. NOT NULL TERMINATED
878 * Location to return result (NULL if result not required)
879 * Last location in string
881 * OUTPUT : Pointer into the string immediately after the token.
882 * NULL if end of string reached or error.
885 get_next_token(char *str
, char **op
, char *str_end
)
887 char *myself
= "get_next_token";
888 char *p
, *tok_start
, *tok_end
;
891 /* Skip leading whitespace */
897 while ((str_end
+ 1 != p
) && (COLON_CHAR
!= *p
)) {
903 /* Required string is now between start and end */
905 *op
= am(myself
, tok_end
- tok_start
+ 2);
907 logmsg(MSG_NOMEM
, LOG_ERR
,
908 "Could not alloc memory for token");
911 strncpy(*op
, tok_start
, tok_end
- tok_start
+ 1);
913 /* Terminate token */
914 (*op
)[tok_end
- tok_start
+ 1] = '\0';
918 /* Check if we reached the end of the input string */
922 /* There is some more */
928 * FUNCTION : free_pwd_entry()
930 * DESCRIPTION: Frees up a pwd_entry structure and its contents.
932 * INPUTS: Pointer to the structure to free.
937 free_pwd_entry(struct passwd_entry
*pwd
)
939 /* Free up strings */
940 if (NULL
!= pwd
->pw_name
)
943 if (NULL
!= pwd
->pw_passwd
)
944 sfree(pwd
->pw_passwd
);
946 if (NULL
!= pwd
->pw_gecos
)
947 sfree(pwd
->pw_gecos
);
949 if (NULL
!= pwd
->pw_shell
)
950 sfree(pwd
->pw_shell
);
952 if (NULL
!= pwd
->pw_dir
)
955 if (NULL
!= pwd
->adjunct_tail
)
956 sfree(pwd
->adjunct_tail
);
958 if (NULL
!= pwd
->pwd_str
)
961 if (NULL
!= pwd
->adjunct_str
)
962 sfree(pwd
->adjunct_str
);
964 /* Free up structure */
969 * FUNCTION : create_pwd_str()
971 * DESCRIPTION: Builds up a new password entity string from a passwd structure.
973 * INPUTS : Structure containing password details
974 * Flag indicating if we should create an adjunct or passwd string.
976 * OUTPUTS : String in malloced memory (to be freed by caller).
980 create_pwd_str(struct passwd_entry
*pwd
, bool_t adjunct
)
982 char *myself
= "create_pwd_str";
986 /* Separator string so we can strcat separator onto things */
987 char sep_str
[2] = {COLON_CHAR
, '\0'};
989 /* Work out the size */
990 len
= strlen(pwd
->pw_name
) + 1;
991 len
+= strlen(pwd
->pw_passwd
) + 1;
993 len
+= strlen(pwd
->adjunct_tail
) + 1;
995 len
+= strlen(pwd
->pw_uid
) + 1;
996 len
+= strlen(pwd
->pw_gid
) + 1;
997 len
+= strlen(pwd
->pw_gecos
) + 1;
998 len
+= strlen(pwd
->pw_dir
) + 1;
999 len
+= strlen(pwd
->pw_shell
) + 1;
1002 /* Allocate some memory for it */
1003 s
= am(myself
, len
);
1007 strcpy(s
, pwd
->pw_name
);
1010 /* Build up a passwd string */
1012 /* If LDAP contains adjunct info then passwd is 'x' */
1015 strcat(s
, pwd
->pw_name
);
1017 strcat(s
, pwd
->pw_passwd
);
1020 strcat(s
, pwd
->pw_uid
);
1022 strcat(s
, pwd
->pw_gid
);
1024 strcat(s
, pwd
->pw_gecos
);
1026 strcat(s
, pwd
->pw_dir
);
1028 strcat(s
, pwd
->pw_shell
);
1030 /* Build up a passwd_adjunct string */
1031 strcat(s
, pwd
->pw_passwd
);
1033 strcat(s
, pwd
->adjunct_tail
);
1040 * FUNCTION: get_old_info()
1042 * DESCRIPTION: Gets as much information as possible from LDAP about one user.
1044 * This goes through the mapping system. This is messy because
1045 * them mapping system will build up a password entry from the
1046 * contents of the DIT. We then have to parse this to recover
1047 * it's individual fields.
1049 * INPUT: Pointer to user name
1052 * OUTPUT: The info in malloced space. To be freed by caller.
1055 struct passwd_entry
*
1056 get_old_info(char *name
, char *domain
)
1058 char *myself
= "get_old_info";
1059 struct passwd_entry
*old_passwd
;
1063 /* Get the password entry */
1065 key
.dsize
= strlen(key
.dptr
);
1066 read_from_dit(PASSWD_MAPPING
, domain
, &key
, &data
);
1067 if (NULL
== data
.dptr
) {
1068 logmsg(MSG_NOTIMECHECK
, LOG_ERR
,
1069 "Could not read old pwd for %s", name
);
1073 /* Pull password apart */
1074 old_passwd
= am(myself
, sizeof (struct passwd_entry
));
1075 if (NULL
== old_passwd
) {
1076 logmsg(MSG_NOMEM
, LOG_ERR
, "Could not alloc for pwd decode");
1082 old_passwd
->pw_name
= NULL
;
1083 old_passwd
->pw_passwd
= NULL
;
1084 old_passwd
->pw_uid
= NULL
;
1085 old_passwd
->pw_gid
= NULL
;
1086 old_passwd
->pw_gecos
= NULL
;
1087 old_passwd
->pw_dir
= NULL
;
1088 old_passwd
->pw_shell
= NULL
;
1089 old_passwd
->adjunct_tail
= NULL
;
1090 old_passwd
->pwd_str
= NULL
;
1091 old_passwd
->adjunct_str
= NULL
;
1092 old_passwd
->adjunct
= FALSE
;
1094 res
= decode_pwd_entry(&data
, old_passwd
, FALSE
);
1096 if (SUCCESS
!= res
) {
1097 free_pwd_entry(old_passwd
);
1101 /* Try to get the adjunct entry */
1102 read_from_dit(PASSWD_ADJUNCT_MAPPING
, domain
, &key
, &data
);
1103 if (NULL
== data
.dptr
) {
1104 /* Fine just no adjunct data */
1105 old_passwd
->adjunct
= FALSE
;
1107 res
= decode_pwd_entry(&data
, old_passwd
, TRUE
);
1109 if (SUCCESS
!= res
) {
1110 free_pwd_entry(old_passwd
);
1115 return (old_passwd
);
1119 * FUNCTION : put_new_info()
1121 * DESCRIPTION: Generates new map strings and puts them back to LDAP
1123 * INPUTS: Info to put back
1126 * OUTPUT: Answer code.
1129 put_new_info(struct passwd_entry
*pwd
, char *domain
)
1133 /* Write it back to LDAP */
1134 data
.dptr
= pwd
->pwd_str
;
1135 data
.dsize
= strlen(data
.dptr
);
1136 key
.dptr
= pwd
->pw_name
;
1137 key
.dsize
= strlen(key
.dptr
);
1138 if (SUCCESS
!= write_to_dit(PASSWD_MAPPING
, domain
, key
, data
,
1143 /* If DIT contains adjunct information do the same for adjunct */
1145 data
.dptr
= pwd
->adjunct_str
;
1146 data
.dsize
= strlen(data
.dptr
);
1147 key
.dptr
= pwd
->pw_name
;
1148 key
.dsize
= strlen(key
.dptr
);
1149 if (SUCCESS
!= write_to_dit(PASSWD_ADJUNCT_MAPPING
, domain
,
1150 key
, data
, TRUE
, FALSE
))
1159 * FUNCTION : get_old_shadow()
1161 * DESCRIPTION :Extracts and decodes shadow information from the DIT
1162 * See also comments under decode_pwd_entry().
1164 * INPUTS : User name
1167 * OUTPUT : Shadow information in malloced memory. To be freed by caller.
1170 get_old_shadow(char *name
, char *domain
)
1172 char *myself
= "get_old_shadow";
1179 key
.dsize
= strlen(key
.dptr
); /* Len excluding terminator */
1180 read_from_dit(AGEING_MAPPING
, domain
, &key
, &data
);
1182 if (NULL
== data
.dptr
) {
1183 /* OK just have no shadow info in DIT */
1187 /* Pull shadow apart */
1188 if (NULL
== (sp
= am(myself
, sizeof (struct spwd
)))) {
1189 logmsg(MSG_NOMEM
, LOG_ERR
,
1190 "Could not alloc for shadow decode");
1197 res
= decode_shadow_entry(&data
, sp
);
1199 if (SUCCESS
!= res
) {
1200 free_shadow_entry(sp
);
1208 * FUNCTION : decode_shadow_entry()
1210 * DESCRIPTION: Pulls apart ageing information. For convenience this is stored
1211 * in a partially filled spwd structure.
1213 * SEE COMMENTS FOR decode_pwd_entry()
1216 decode_shadow_entry(datum
*data
, struct spwd
*sp
)
1218 char *p
, *str_end
, *temp
;
1220 /* Work out last location in string */
1221 str_end
= data
->dptr
+ data
->dsize
;
1224 if (NULL
== (p
= get_next_token(data
->dptr
, &(sp
->sp_namp
), str_end
)))
1227 /* date of last change */
1228 if (NULL
== (p
= get_next_token(p
, &temp
, str_end
)))
1230 sp
->sp_lstchg
= atoi(temp
);
1232 /* min days to passwd change */
1233 if (NULL
== (p
= get_next_token(p
, &temp
, str_end
)))
1235 sp
->sp_min
= atoi(temp
);
1237 /* max days to passwd change */
1238 if (NULL
== (p
= get_next_token(p
, &temp
, str_end
)))
1240 sp
->sp_max
= atoi(temp
);
1242 /* warning period */
1243 if (NULL
== (p
= get_next_token(p
, &temp
, str_end
)))
1245 sp
->sp_warn
= atoi(temp
);
1247 /* max days inactive */
1248 if (NULL
== (p
= get_next_token(p
, &temp
, str_end
)))
1250 sp
->sp_inact
= atoi(temp
);
1252 /* account expiry date */
1253 if (NULL
== (p
= get_next_token(p
, &temp
, str_end
)))
1255 sp
->sp_expire
= atoi(temp
);
1258 if (NULL
!= (p
= get_next_token(p
, &temp
, str_end
)))
1260 sp
->sp_flag
= atoi(temp
);
1266 * FUNCTION : write_shadow_info()
1268 * DESCRIPTION: Writes shadow information back to the DIT.
1271 * Information to write
1273 * OUTPUT : Success code
1277 write_shadow_info(char *domain
, struct spwd
*sp
)
1279 char *myself
= "write_shadow_info";
1285 /* Work out how long string will be */
1286 len
= strlen(sp
->sp_namp
) + 1;
1289 * Bit crude but if we assume 1 byte is 3 decimal characters
1290 * will get enough buffer for the longs and some spare.
1292 len
+= 7 * (3 * sizeof (long) + 1);
1294 /* Allocate some memory */
1295 str
= am(myself
, len
);
1297 logmsg(MSG_NOMEM
, LOG_ERR
, "Could not aloc for shadow write");
1301 /* Build up shadow string */
1302 sprintf(str
, "%s%c%d%c%d%c%d%c%d%c%d%c%d%c%d",
1303 sp
->sp_namp
, COLON_CHAR
,
1304 sp
->sp_lstchg
, COLON_CHAR
,
1305 sp
->sp_min
, COLON_CHAR
,
1306 sp
->sp_max
, COLON_CHAR
,
1307 sp
->sp_warn
, COLON_CHAR
,
1308 sp
->sp_inact
, COLON_CHAR
,
1309 sp
->sp_expire
, COLON_CHAR
,
1314 data
.dsize
= strlen(data
.dptr
);
1315 key
.dptr
= sp
->sp_namp
;
1316 key
.dsize
= strlen(key
.dptr
);
1317 res
= write_to_dit(AGEING_MAPPING
, domain
, key
, data
, TRUE
, FALSE
);
1324 * FUNCTION : free_shadow_entry()
1326 * DESCRIPTION: Frees up a shadow information structure
1328 * INPUTS : Structure to free
1333 free_shadow_entry(struct spwd
*spwd
)
1335 if (NULL
!= spwd
->sp_namp
)
1336 sfree(spwd
->sp_namp
);
1338 if (NULL
!= spwd
->sp_pwdp
)
1339 sfree(spwd
->sp_pwdp
);
1341 /* No need to free numerics */
1343 /* Free up structure */