1 /*	$NetBSD: homedir.c,v 1.2 2021/08/14 16:15:02 christos Exp $	*/
2 
3 /* homedir.c - create/remove user home directories */
4 /* $OpenLDAP$ */
5 /* This work is part of OpenLDAP Software <http://www.openldap.org/>.
6  *
7  * Copyright 2009-2010 The OpenLDAP Foundation.
8  * Portions copyright 2009-2010 Symas Corporation.
9  * All rights reserved.
10  *
11  * Redistribution and use in source and binary forms, with or without
12  * modification, are permitted only as authorized by the OpenLDAP
13  * Public License.
14  *
15  * A copy of this license is available in the file LICENSE in the
16  * top-level directory of the distribution or, alternatively, at
17  * <http://www.OpenLDAP.org/license.html>.
18  */
19 /* ACKNOWLEDGEMENTS:
20  * This work was initially developed by Emily Backes at Symas
21  * Corp. for inclusion in OpenLDAP Software.
22  */
23 
24 #include <sys/cdefs.h>
25 __RCSID("$NetBSD: homedir.c,v 1.2 2021/08/14 16:15:02 christos Exp $");
26 
27 #include "portable.h"
28 
29 #ifdef SLAPD_OVER_HOMEDIR
30 
31 #define _FILE_OFFSET_BITS 64
32 
33 #include <stdio.h>
34 #include <fcntl.h>
35 
36 #include <ac/string.h>
37 #include <ac/ctype.h>
38 #include <ac/errno.h>
39 #include <sys/stat.h>
40 #include <ac/unistd.h>
41 #include <ac/dirent.h>
42 #include <ac/time.h>
43 
44 #include "slap.h"
45 #include "slap-config.h"
46 
47 #define DEFAULT_MIN_UID ( 100 )
48 #define DEFAULT_SKEL ( LDAP_DIRSEP "etc" LDAP_DIRSEP "skel" )
49 
50 typedef struct homedir_regexp {
51 	char *match;
52 	char *replace;
53 	regex_t compiled;
54 	struct homedir_regexp *next;
55 } homedir_regexp;
56 
57 typedef enum {
58 	DEL_IGNORE,
59 	DEL_DELETE,
60 	DEL_ARCHIVE
61 } delete_style;
62 
63 typedef struct homedir_data {
64 	char *skeleton_path;
65 	unsigned min_uid;
66 	AttributeDescription *home_ad;
67 	AttributeDescription *uidn_ad;
68 	AttributeDescription *gidn_ad;
69 	homedir_regexp *regexps;
70 	delete_style style;
71 	char *archive_path;
72 } homedir_data;
73 
74 typedef struct homedir_cb_data {
75 	slap_overinst *on;
76 	Entry *entry;
77 } homedir_cb_data;
78 
79 typedef struct name_list {
80 	char *name;
81 	struct stat st;
82 	struct name_list *next;
83 } name_list;
84 
85 typedef struct name_list_list {
86 	name_list *list;
87 	struct name_list_list *next;
88 } name_list_list;
89 
90 typedef enum {
91 	TRAVERSE_CB_CONTINUE,
92 	TRAVERSE_CB_DONE,
93 	TRAVERSE_CB_FAIL
94 } traverse_cb_ret;
95 
96 /* private, file info, context */
97 typedef traverse_cb_ret (*traverse_cb_func)(
98 		void *,
99 		const char *,
100 		const struct stat *,
101 		void * );
102 typedef struct traverse_cb {
103 	traverse_cb_func pre_func;
104 	traverse_cb_func post_func;
105 	void *pre_private;
106 	void *post_private;
107 } traverse_cb;
108 
109 typedef struct copy_private {
110 	int source_prefix_len;
111 	const char *dest_prefix;
112 	int dest_prefix_len;
113 	uid_t uidn;
114 	gid_t gidn;
115 } copy_private;
116 
117 typedef struct chown_private {
118 	uid_t old_uidn;
119 	uid_t new_uidn;
120 	gid_t old_gidn;
121 	gid_t new_gidn;
122 } chown_private;
123 
124 typedef struct ustar_header {
125 	char name[100];
126 	char mode[8];
127 	char uid[8];
128 	char gid[8];
129 	char size[12];
130 	char mtime[12];
131 	char checksum[8];
132 	char typeflag[1];
133 	char linkname[100];
134 	char magic[6];
135 	char version[2];
136 	char uname[32];
137 	char gname[32];
138 	char devmajor[8];
139 	char devminor[8];
140 	char prefix[155];
141 	char pad[12];
142 } ustar_header;
143 
144 typedef struct tar_private {
145 	FILE *file;
146 	const char *name;
147 } tar_private;
148 
149 /* FIXME: This mutex really needs to be executable-global, but this
150  * will have to do for now.
151  */
152 static ldap_pvt_thread_mutex_t readdir_mutex;
153 static ConfigDriver homedir_regexp_cfg;
154 static ConfigDriver homedir_style_cfg;
155 static slap_overinst homedir;
156 
157 static ConfigTable homedircfg[] = {
158 	{ "homedir-skeleton-path", "pathname", 2, 2, 0,
159 		ARG_STRING|ARG_OFFSET,
160 		(void *)offsetof(homedir_data, skeleton_path),
161 		"( OLcfgCtAt:8.1 "
162 			"NAME 'olcSkeletonPath' "
163 			"DESC 'Pathname for home directory skeleton template' "
164 			"SYNTAX OMsDirectoryString "
165 			"SINGLE-VALUE )",
166 		NULL, { .v_string = DEFAULT_SKEL }
167 	},
168 
169 	{ "homedir-min-uidnumber", "uid number", 2, 2, 0,
170 		ARG_UINT|ARG_OFFSET,
171 		(void *)offsetof(homedir_data, min_uid),
172 		"( OLcfgCtAt:8.2 "
173 			"NAME 'olcMinimumUidNumber' "
174 			"DESC 'Minimum uidNumber attribute to consider' "
175 			"SYNTAX OMsInteger "
176 			"SINGLE-VALUE )",
177 		NULL, { .v_uint = DEFAULT_MIN_UID }
178 	},
179 
180 	{ "homedir-regexp", "regexp> <path", 3, 3, 0,
181 		ARG_MAGIC,
182 		homedir_regexp_cfg,
183 		"( OLcfgCtAt:8.3 "
184 			"NAME 'olcHomedirRegexp' "
185 			"DESC 'Regular expression for matching and transforming paths' "
186 			"SYNTAX OMsDirectoryString "
187 			"X-ORDERED 'VALUES' )",
188 		NULL, NULL
189 	},
190 
191 	{ "homedir-delete-style", "style", 2, 2, 0,
192 		ARG_MAGIC,
193 		homedir_style_cfg,
194 		"( OLcfgCtAt:8.4 "
195 			"NAME 'olcHomedirDeleteStyle' "
196 			"DESC 'Action to perform when removing a home directory' "
197 			"SYNTAX OMsDirectoryString "
198 			"SINGLE-VALUE )",
199 		NULL, NULL
200 	},
201 
202 	{ "homedir-archive-path", "pathname", 2, 2, 0,
203 		ARG_STRING|ARG_OFFSET,
204 		(void *)offsetof(homedir_data, archive_path),
205 		"( OLcfgCtAt:8.5 "
206 			"NAME 'olcHomedirArchivePath' "
207 			"DESC 'Pathname for home directory archival' "
208 			"SYNTAX OMsDirectoryString "
209 			"SINGLE-VALUE )",
210 		NULL, NULL
211 	},
212 
213 	{ NULL, NULL, 0, 0, 0, ARG_IGNORED }
214 };
215 
216 static ConfigOCs homedirocs[] = {
217 	{ "( OLcfgCtOc:8.1 "
218 		"NAME 'olcHomedirConfig' "
219 			"DESC 'Homedir configuration' "
220 			"SUP olcOverlayConfig "
221 			"MAY ( olcSkeletonPath $ olcMinimumUidNumber "
222 				"$ olcHomedirRegexp $ olcHomedirDeleteStyle "
223 				"$ olcHomedirArchivePath ) )",
224 		Cft_Overlay, homedircfg },
225 
226 	{ NULL, 0, NULL }
227 };
228 
229 static int
homedir_regexp_cfg(ConfigArgs * c)230 homedir_regexp_cfg( ConfigArgs *c )
231 {
232 	slap_overinst *on = (slap_overinst *)c->bi;
233 	homedir_data *data = (homedir_data *)on->on_bi.bi_private;
234 	int rc = ARG_BAD_CONF;
235 
236 	assert( data != NULL );
237 
238 	switch ( c->op ) {
239 		case SLAP_CONFIG_EMIT: {
240 			int i;
241 			homedir_regexp *r;
242 			struct berval bv;
243 			char buf[4096];
244 
245 			bv.bv_val = buf;
246 			for ( i = 0, r = data->regexps; r != NULL; ++i, r = r->next ) {
247 				bv.bv_len = snprintf( buf, sizeof(buf), "{%d}%s %s", i,
248 						r->match, r->replace );
249 				if ( bv.bv_len >= sizeof(buf) ) {
250 					Debug( LDAP_DEBUG_ANY, "homedir_regexp_cfg: "
251 							"emit serialization failed: size %lu\n",
252 							(unsigned long)bv.bv_len );
253 					return ARG_BAD_CONF;
254 				}
255 				value_add_one( &c->rvalue_vals, &bv );
256 			}
257 			rc = 0;
258 		} break;
259 
260 		case LDAP_MOD_DELETE:
261 			if ( c->valx < 0 ) { /* delete all values */
262 				homedir_regexp *r, *rnext;
263 
264 				for ( r = data->regexps; r != NULL; r = rnext ) {
265 					rnext = r->next;
266 					ch_free( r->match );
267 					ch_free( r->replace );
268 					regfree( &r->compiled );
269 					ch_free( r );
270 				}
271 				data->regexps = NULL;
272 				rc = 0;
273 
274 			} else { /* delete value by index*/
275 				homedir_regexp **rp, *r;
276 				int i;
277 
278 				for ( i = 0, rp = &data->regexps; i < c->valx;
279 						++i, rp = &(*rp)->next )
280 					;
281 
282 				r = *rp;
283 				*rp = r->next;
284 				ch_free( r->match );
285 				ch_free( r->replace );
286 				regfree( &r->compiled );
287 				ch_free( r );
288 
289 				rc = 0;
290 			}
291 			break;
292 
293 		case LDAP_MOD_ADD:		/* fallthrough */
294 		case SLAP_CONFIG_ADD: { /* add values */
295 			char *match = c->argv[1];
296 			char *replace = c->argv[2];
297 			regex_t compiled;
298 			homedir_regexp **rp, *r;
299 
300 			memset( &compiled, 0, sizeof(compiled) );
301 			rc = regcomp( &compiled, match, REG_EXTENDED );
302 			if ( rc ) {
303 				regerror( rc, &compiled, c->cr_msg, sizeof(c->cr_msg) );
304 				regfree( &compiled );
305 				return ARG_BAD_CONF;
306 			}
307 
308 			r = ch_calloc( 1, sizeof(homedir_regexp) );
309 			r->match = strdup( match );
310 			r->replace = strdup( replace );
311 			r->compiled = compiled;
312 
313 			if ( c->valx == -1 ) { /* append */
314 				for ( rp = &data->regexps; ( *rp ) != NULL;
315 						rp = &(*rp)->next )
316 					;
317 				*rp = r;
318 
319 			} else { /* insert at valx */
320 				int i;
321 				for ( i = 0, rp = &data->regexps; i < c->valx;
322 						rp = &(*rp)->next, ++i )
323 					;
324 				r->next = *rp;
325 				*rp = r;
326 			}
327 			rc = 0;
328 			break;
329 		}
330 		default:
331 			abort();
332 	}
333 
334 	return rc;
335 }
336 
337 static int
homedir_style_cfg(ConfigArgs * c)338 homedir_style_cfg( ConfigArgs *c )
339 {
340 	slap_overinst *on = (slap_overinst *)c->bi;
341 	homedir_data *data = (homedir_data *)on->on_bi.bi_private;
342 	int rc = ARG_BAD_CONF;
343 	struct berval bv;
344 
345 	assert( data != NULL );
346 
347 	switch ( c->op ) {
348 		case SLAP_CONFIG_EMIT:
349 			bv.bv_val = data->style == DEL_IGNORE ? "IGNORE" :
350 					data->style == DEL_DELETE	  ? "DELETE" :
351 													"ARCHIVE";
352 			bv.bv_len = strlen( bv.bv_val );
353 			rc = value_add_one( &c->rvalue_vals, &bv );
354 			if ( rc != 0 ) return ARG_BAD_CONF;
355 			break;
356 
357 		case LDAP_MOD_DELETE:
358 			data->style = DEL_IGNORE;
359 			rc = 0;
360 			break;
361 
362 		case LDAP_MOD_ADD:	  /* fallthrough */
363 		case SLAP_CONFIG_ADD: /* add values */
364 			if ( strcasecmp( c->argv[1], "IGNORE" ) == 0 )
365 				data->style = DEL_IGNORE;
366 			else if ( strcasecmp( c->argv[1], "DELETE" ) == 0 )
367 				data->style = DEL_DELETE;
368 			else if ( strcasecmp( c->argv[1], "ARCHIVE" ) == 0 )
369 				data->style = DEL_ARCHIVE;
370 			else {
371 				Debug( LDAP_DEBUG_ANY, "homedir_style_cfg: "
372 						"unrecognized style keyword\n" );
373 				return ARG_BAD_CONF;
374 			}
375 			rc = 0;
376 			break;
377 
378 		default:
379 			abort();
380 	}
381 
382 	return rc;
383 }
384 
385 #define HOMEDIR_NULLWRAP(x) ( ( x ) == NULL ? "unknown" : (x) )
386 static void
report_errno(const char * parent_func,const char * func,const char * filename)387 report_errno( const char *parent_func, const char *func, const char *filename )
388 {
389 	int save_errno = errno;
390 	char ebuf[1024];
391 
392 	Debug( LDAP_DEBUG_ANY, "homedir: "
393 			"%s: %s: \"%s\": %d (%s)\n",
394 			HOMEDIR_NULLWRAP(parent_func), HOMEDIR_NULLWRAP(func),
395 			HOMEDIR_NULLWRAP(filename), save_errno,
396 			AC_STRERROR_R( save_errno, ebuf, sizeof(ebuf) ) );
397 }
398 
399 static int
copy_link(const char * dest_file,const char * source_file,const struct stat * st,uid_t uidn,gid_t gidn,void * ctx)400 copy_link(
401 		const char *dest_file,
402 		const char *source_file,
403 		const struct stat *st,
404 		uid_t uidn,
405 		gid_t gidn,
406 		void *ctx )
407 {
408 	char *buf = NULL;
409 	int rc;
410 
411 	assert( dest_file != NULL );
412 	assert( source_file != NULL );
413 	assert( st != NULL );
414 	assert( (st->st_mode & S_IFMT) == S_IFLNK );
415 
416 	Debug( LDAP_DEBUG_TRACE, "homedir: "
417 			"copy_link: %s to %s\n",
418 			source_file, dest_file );
419 	Debug( LDAP_DEBUG_TRACE, "homedir: "
420 			"copy_link: %s uid %ld gid %ld\n",
421 			dest_file, (long)uidn, (long)gidn );
422 
423 	/* calloc +1 for terminator */
424 	buf = ber_memcalloc_x( 1, st->st_size + 1, ctx );
425 	if ( buf == NULL ) {
426 		Debug( LDAP_DEBUG_ANY, "homedir: "
427 				"copy_link: alloc failed\n" );
428 		return 1;
429 	}
430 	rc = readlink( source_file, buf, st->st_size );
431 	if ( rc == -1 ) {
432 		report_errno( "copy_link", "readlink", source_file );
433 		goto fail;
434 	}
435 	rc = symlink( buf, dest_file );
436 	if ( rc ) {
437 		report_errno( "copy_link", "symlink", dest_file );
438 		goto fail;
439 	}
440 	rc = lchown( dest_file, uidn, gidn );
441 	if ( rc ) {
442 		report_errno( "copy_link", "lchown", dest_file );
443 		goto fail;
444 	}
445 	goto out;
446 
447 fail:
448 	rc = 1;
449 
450 out:
451 	if ( buf != NULL ) ber_memfree_x( buf, ctx );
452 	return rc;
453 }
454 
455 static int
copy_blocks(FILE * source,FILE * dest,const char * source_file,const char * dest_file)456 copy_blocks(
457 		FILE *source,
458 		FILE *dest,
459 		const char *source_file,
460 		const char *dest_file )
461 {
462 	char buf[4096];
463 	size_t nread = 0;
464 	int done = 0;
465 
466 	while ( !done ) {
467 		nread = fread( buf, 1, sizeof(buf), source );
468 		if ( nread == 0 ) {
469 			if ( feof( source ) ) {
470 				done = 1;
471 			} else if ( ferror( source ) ) {
472 				if ( source_file != NULL )
473 					Debug( LDAP_DEBUG_ANY, "homedir: "
474 							"read error on %s\n",
475 							source_file );
476 				goto fail;
477 			}
478 		} else {
479 			size_t nwritten = 0;
480 			nwritten = fwrite( buf, 1, nread, dest );
481 			if ( nwritten < nread ) {
482 				if ( dest_file != NULL )
483 					Debug( LDAP_DEBUG_ANY, "homedir: "
484 							"write error on %s\n",
485 							dest_file );
486 				goto fail;
487 			}
488 		}
489 	}
490 	return 0;
491 fail:
492 	return 1;
493 }
494 
495 static int
copy_file(const char * dest_file,const char * source_file,uid_t uid,gid_t gid,int mode)496 copy_file(
497 		const char *dest_file,
498 		const char *source_file,
499 		uid_t uid,
500 		gid_t gid,
501 		int mode )
502 {
503 	FILE *source = NULL;
504 	FILE *dest = NULL;
505 	int rc;
506 
507 	assert( dest_file != NULL );
508 	assert( source_file != NULL );
509 
510 	Debug( LDAP_DEBUG_TRACE, "homedir: "
511 			"copy_file: %s to %s mode 0%o\n",
512 			source_file, dest_file, mode );
513 	Debug( LDAP_DEBUG_TRACE, "homedir: "
514 			"copy_file: %s uid %ld gid %ld\n",
515 			dest_file, (long)uid, (long)gid );
516 
517 	source = fopen( source_file, "rb" );
518 	if ( source == NULL ) {
519 		report_errno( "copy_file", "fopen", source_file );
520 		goto fail;
521 	}
522 	dest = fopen( dest_file, "wb" );
523 	if ( dest == NULL ) {
524 		report_errno( "copy_file", "fopen", dest_file );
525 		goto fail;
526 	}
527 
528 	rc = copy_blocks( source, dest, source_file, dest_file );
529 	if ( rc != 0 ) goto fail;
530 
531 	fclose( source );
532 	source = NULL;
533 	rc = fclose( dest );
534 	dest = NULL;
535 	if ( rc != 0 ) {
536 		report_errno( "copy_file", "fclose", dest_file );
537 		goto fail;
538 	}
539 
540 	/* set owner/permission */
541 	rc = lchown( dest_file, uid, gid );
542 	if ( rc != 0 ) {
543 		report_errno( "copy_file", "lchown", dest_file );
544 		goto fail;
545 	}
546 	rc = chmod( dest_file, mode );
547 	if ( rc != 0 ) {
548 		report_errno( "copy_file", "chmod", dest_file );
549 		goto fail;
550 	}
551 
552 	rc = 0;
553 	goto out;
554 fail:
555 	rc = 1;
556 out:
557 	if ( source != NULL ) fclose( source );
558 	if ( dest != NULL ) fclose( dest );
559 	Debug( LDAP_DEBUG_TRACE, "homedir: "
560 			"copy_file: %s to %s exit %d\n",
561 			source_file, dest_file, rc );
562 	return rc;
563 }
564 
565 static void
free_name_list(name_list * names,void * ctx)566 free_name_list( name_list *names, void *ctx )
567 {
568 	name_list *next;
569 
570 	while ( names != NULL ) {
571 		next = names->next;
572 		if ( names->name != NULL ) ber_memfree_x( names->name, ctx );
573 		ber_memfree_x( names, ctx );
574 		names = next;
575 	}
576 }
577 
578 static int
grab_names(const char * dir_path,name_list ** names,void * ctx)579 grab_names( const char *dir_path, name_list **names, void *ctx )
580 {
581 	int locked = 0;
582 	DIR *dir = NULL;
583 	struct dirent *entry = NULL;
584 	name_list **tail = NULL;
585 	int dir_path_len = 0;
586 	int rc = 0;
587 
588 	assert( dir_path != NULL );
589 	assert( names != NULL );
590 	assert( *names == NULL );
591 
592 	Debug( LDAP_DEBUG_TRACE, "homedir: "
593 			"grab_names: %s\n", dir_path );
594 
595 	tail = names;
596 	dir_path_len = strlen( dir_path );
597 	ldap_pvt_thread_mutex_lock( &readdir_mutex );
598 	locked = 1;
599 
600 	dir = opendir( dir_path );
601 	if ( dir == NULL ) {
602 		report_errno( "grab_names", "opendir", dir_path );
603 		goto fail;
604 	}
605 
606 	while ( ( entry = readdir( dir ) ) != NULL ) {
607 		/* no d_namelen in ac/dirent.h */
608 		int d_namelen = strlen( entry->d_name );
609 		int full_len;
610 
611 		/* Skip . and .. */
612 		if ( ( d_namelen == 1 && entry->d_name[0] == '.' ) ||
613 				( d_namelen == 2 && entry->d_name[0] == '.' &&
614 						entry->d_name[1] == '.' ) ) {
615 			continue;
616 		}
617 
618 		*tail = ber_memcalloc_x( 1, sizeof(**tail), ctx );
619 		if ( *tail == NULL ) {
620 			Debug( LDAP_DEBUG_ANY, "homedir: "
621 					"grab_names: list alloc failed\n" );
622 			goto fail;
623 		}
624 		(*tail)->next = NULL;
625 
626 		/* +1 for dirsep, +1 for term */
627 		full_len = dir_path_len + 1 + d_namelen + 1;
628 		(*tail)->name = ber_memalloc_x( full_len, ctx );
629 		if ( (*tail)->name == NULL ) {
630 			Debug( LDAP_DEBUG_ANY, "homedir: "
631 					"grab_names: name alloc failed\n" );
632 			goto fail;
633 		}
634 		snprintf( (*tail)->name, full_len, "%s" LDAP_DIRSEP "%s",
635 				dir_path, entry->d_name );
636 		Debug( LDAP_DEBUG_TRACE, "homedir: "
637 				"grab_names: found \"%s\"\n",
638 				(*tail)->name );
639 
640 		rc = lstat( (*tail)->name, &(*tail)->st );
641 		if ( rc ) {
642 			report_errno( "grab_names", "lstat", (*tail)->name );
643 			goto fail;
644 		}
645 
646 		tail = &(*tail)->next;
647 	}
648 	closedir( dir );
649 	ldap_pvt_thread_mutex_unlock( &readdir_mutex );
650 	locked = 0;
651 
652 	dir = NULL;
653 	goto success;
654 
655 success:
656 	rc = 0;
657 	goto out;
658 fail:
659 	rc = 1;
660 	goto out;
661 out:
662 	if ( dir != NULL ) closedir( dir );
663 	if ( locked ) ldap_pvt_thread_mutex_unlock( &readdir_mutex );
664 	if ( rc != 0 && *names != NULL ) {
665 		free_name_list( *names, ctx );
666 		*names = NULL;
667 	}
668 	Debug( LDAP_DEBUG_TRACE, "homedir: "
669 			"grab_names: %s exit %d\n",
670 			dir_path, rc );
671 	return rc;
672 }
673 
674 static int
traverse(const char * path,const traverse_cb * cb,void * ctx)675 traverse( const char *path, const traverse_cb *cb, void *ctx )
676 {
677 	name_list *next_name = NULL;
678 	name_list_list *dir_stack = NULL;
679 	name_list_list *next_dir;
680 	int rc = 0;
681 
682 	assert( path != NULL );
683 	assert( cb != NULL );
684 	assert( cb->pre_func || cb->post_func );
685 
686 	Debug( LDAP_DEBUG_TRACE, "homedir: "
687 			"traverse: %s\n", path );
688 
689 	dir_stack = ber_memcalloc_x( 1, sizeof(*dir_stack), ctx );
690 	if ( dir_stack == NULL ) goto alloc_fail;
691 	dir_stack->next = NULL;
692 	dir_stack->list = ber_memcalloc_x( 1, sizeof(name_list), ctx );
693 	if ( dir_stack->list == NULL ) goto alloc_fail;
694 	rc = lstat( path, &dir_stack->list->st );
695 	if ( rc != 0 ) {
696 		report_errno( "traverse", "lstat", path );
697 		goto fail;
698 	}
699 	dir_stack->list->next = NULL;
700 	dir_stack->list->name = ber_strdup_x( path, ctx );
701 	if ( dir_stack->list->name == NULL ) goto alloc_fail;
702 
703 	while ( dir_stack != NULL ) {
704 		while ( dir_stack->list != NULL ) {
705 			Debug( LDAP_DEBUG_TRACE, "homedir: "
706 					"traverse: top of loop with \"%s\"\n",
707 					dir_stack->list->name );
708 
709 			if ( cb->pre_func != NULL ) {
710 				traverse_cb_ret cb_rc;
711 				cb_rc = cb->pre_func( cb->pre_private, dir_stack->list->name,
712 						&dir_stack->list->st, ctx );
713 
714 				if ( cb_rc == TRAVERSE_CB_DONE ) goto cb_done;
715 				if ( cb_rc == TRAVERSE_CB_FAIL ) goto cb_fail;
716 			}
717 			if ( (dir_stack->list->st.st_mode & S_IFMT) == S_IFDIR ) {
718 				/* push dir onto stack */
719 				next_dir = dir_stack;
720 				dir_stack = ber_memalloc_x( sizeof(*dir_stack), ctx );
721 				if ( dir_stack == NULL ) {
722 					dir_stack = next_dir;
723 					goto alloc_fail;
724 				}
725 				dir_stack->list = NULL;
726 				dir_stack->next = next_dir;
727 				rc = grab_names(
728 						dir_stack->next->list->name, &dir_stack->list, ctx );
729 				if ( rc != 0 ) {
730 					Debug( LDAP_DEBUG_ANY, "homedir: "
731 							"traverse: grab_names %s failed\n",
732 							dir_stack->next->list->name );
733 					goto fail;
734 				}
735 			} else {
736 				/* just a file */
737 				if ( cb->post_func != NULL ) {
738 					traverse_cb_ret cb_rc;
739 					cb_rc = cb->post_func( cb->post_private,
740 							dir_stack->list->name, &dir_stack->list->st, ctx );
741 
742 					if ( cb_rc == TRAVERSE_CB_DONE ) goto cb_done;
743 					if ( cb_rc == TRAVERSE_CB_FAIL ) goto cb_fail;
744 				}
745 				next_name = dir_stack->list->next;
746 				ber_memfree_x( dir_stack->list->name, ctx );
747 				ber_memfree_x( dir_stack->list, ctx );
748 				dir_stack->list = next_name;
749 			}
750 		}
751 		/* Time to pop a directory off the stack */
752 		next_dir = dir_stack->next;
753 		ber_memfree_x( dir_stack, ctx );
754 		dir_stack = next_dir;
755 		if ( dir_stack != NULL ) {
756 			if ( cb->post_func != NULL ) {
757 				traverse_cb_ret cb_rc;
758 				cb_rc = cb->post_func( cb->post_private, dir_stack->list->name,
759 						&dir_stack->list->st, ctx );
760 
761 				if ( cb_rc == TRAVERSE_CB_DONE ) goto cb_done;
762 				if ( cb_rc == TRAVERSE_CB_FAIL ) goto cb_fail;
763 			}
764 			next_name = dir_stack->list->next;
765 			ber_memfree_x( dir_stack->list->name, ctx );
766 			ber_memfree_x( dir_stack->list, ctx );
767 			dir_stack->list = next_name;
768 		}
769 	}
770 
771 	goto success;
772 
773 cb_done:
774 	Debug( LDAP_DEBUG_TRACE, "homedir: "
775 			"traverse: cb signaled completion\n" );
776 success:
777 	rc = 0;
778 	goto out;
779 
780 cb_fail:
781 	Debug( LDAP_DEBUG_ANY, "homedir: "
782 			"traverse: cb signaled failure\n" );
783 	goto fail;
784 alloc_fail:
785 	Debug( LDAP_DEBUG_ANY, "homedir: "
786 			"traverse: allocation failed\n" );
787 fail:
788 	rc = 1;
789 	goto out;
790 
791 out:
792 	while ( dir_stack != NULL ) {
793 		free_name_list( dir_stack->list, ctx );
794 		next_dir = dir_stack->next;
795 		ber_memfree_x( dir_stack, ctx );
796 		dir_stack = next_dir;
797 	}
798 	return rc;
799 }
800 
801 static traverse_cb_ret
traverse_copy_pre(void * private,const char * name,const struct stat * st,void * ctx)802 traverse_copy_pre(
803 		void *private,
804 		const char *name,
805 		const struct stat *st,
806 		void *ctx )
807 {
808 	copy_private *cp = private;
809 	char *dest_name = NULL;
810 	int source_name_len;
811 	int dest_name_len;
812 	int rc;
813 
814 	assert( private != NULL );
815 	assert( name != NULL );
816 	assert( st != NULL );
817 
818 	Debug( LDAP_DEBUG_TRACE, "homedir: "
819 			"traverse_copy_pre: %s entering\n",
820 			name );
821 
822 	assert( cp->source_prefix_len >= 0 );
823 	assert( cp->dest_prefix != NULL );
824 	assert( cp->dest_prefix_len > 1 );
825 
826 	source_name_len = strlen( name );
827 	assert( source_name_len >= cp->source_prefix_len );
828 	/* +1 for terminator */
829 	dest_name_len =
830 			source_name_len + cp->dest_prefix_len - cp->source_prefix_len + 1;
831 	dest_name = ber_memalloc_x( dest_name_len, ctx );
832 	if ( dest_name == NULL ) goto alloc_fail;
833 
834 	snprintf( dest_name, dest_name_len, "%s%s", cp->dest_prefix,
835 			name + cp->source_prefix_len );
836 
837 	switch ( st->st_mode & S_IFMT ) {
838 		case S_IFDIR:
839 			rc = mkdir( dest_name, st->st_mode & 06775 );
840 			if ( rc ) {
841 				int save_errno = errno;
842 				switch ( save_errno ) {
843 					case EEXIST:
844 						/* directory already present; nothing to do */
845 						goto exists;
846 						break;
847 					case ENOENT:
848 						/* FIXME: should mkdir -p here */
849 						/* fallthrough for now */
850 					default:
851 						report_errno( "traverse_copy_pre", "mkdir", dest_name );
852 						goto fail;
853 				}
854 			}
855 			rc = lchown( dest_name, cp->uidn, cp->gidn );
856 			if ( rc ) {
857 				report_errno( "traverse_copy_pre", "lchown", dest_name );
858 				goto fail;
859 			}
860 			rc = chmod( dest_name, st->st_mode & 07777 );
861 			if ( rc ) {
862 				report_errno( "traverse_copy_pre", "chmod", dest_name );
863 				goto fail;
864 			}
865 			break;
866 		case S_IFREG:
867 			rc = copy_file(
868 					dest_name, name, cp->uidn, cp->gidn, st->st_mode & 07777 );
869 			if ( rc ) goto fail;
870 			break;
871 		case S_IFIFO:
872 			rc = mkfifo( dest_name, 0700 );
873 			if ( rc ) {
874 				report_errno( "traverse_copy_pre", "mkfifo", dest_name );
875 				goto fail;
876 			}
877 			rc = lchown( dest_name, cp->uidn, cp->gidn );
878 			if ( rc ) {
879 				report_errno( "traverse_copy_pre", "lchown", dest_name );
880 				goto fail;
881 			}
882 			rc = chmod( dest_name, st->st_mode & 07777 );
883 			if ( rc ) {
884 				report_errno( "traverse_copy_pre", "chmod", dest_name );
885 				goto fail;
886 			}
887 			break;
888 		case S_IFLNK:
889 			rc = copy_link( dest_name, name, st, cp->uidn, cp->gidn, ctx );
890 			if ( rc ) goto fail;
891 			break;
892 		default:
893 			Debug( LDAP_DEBUG_TRACE, "homedir: "
894 					"traverse_copy_pre: skipping special: %s\n",
895 					name );
896 	}
897 
898 	goto success;
899 
900 alloc_fail:
901 	Debug( LDAP_DEBUG_ANY, "homedir: "
902 			"traverse_copy_pre: allocation failed\n" );
903 fail:
904 	rc = TRAVERSE_CB_FAIL;
905 	goto out;
906 
907 exists:
908 	Debug( LDAP_DEBUG_TRACE, "homedir: "
909 			"traverse_copy_pre: \"%s\" already exists,"
910 			" skipping the rest\n",
911 			dest_name );
912 	rc = TRAVERSE_CB_DONE;
913 	goto out;
914 
915 success:
916 	rc = TRAVERSE_CB_CONTINUE;
917 out:
918 	if ( dest_name != NULL ) ber_memfree_x( dest_name, ctx );
919 	Debug( LDAP_DEBUG_TRACE, "homedir: "
920 			"traverse_copy_pre: exit %d\n", rc );
921 	return rc;
922 }
923 
924 static int
copy_tree(const char * dest_path,const char * source_path,uid_t uidn,gid_t gidn,void * ctx)925 copy_tree(
926 		const char *dest_path,
927 		const char *source_path,
928 		uid_t uidn,
929 		gid_t gidn,
930 		void *ctx )
931 {
932 	traverse_cb cb;
933 	copy_private cp;
934 	int rc;
935 
936 	assert( dest_path != NULL );
937 	assert( source_path != NULL );
938 
939 	Debug( LDAP_DEBUG_TRACE, "homedir: "
940 			"copy_tree: %s to %s entering\n",
941 			source_path, dest_path );
942 
943 	cb.pre_func = traverse_copy_pre;
944 	cb.post_func = NULL;
945 	cb.pre_private = &cp;
946 	cb.post_private = NULL;
947 
948 	cp.source_prefix_len = strlen( source_path );
949 	cp.dest_prefix = dest_path;
950 	cp.dest_prefix_len = strlen( dest_path );
951 	cp.uidn = uidn;
952 	cp.gidn = gidn;
953 
954 	if ( cp.source_prefix_len <= cp.dest_prefix_len &&
955 			strncmp( source_path, dest_path, cp.source_prefix_len ) == 0 &&
956 			( cp.source_prefix_len == cp.dest_prefix_len ||
957 					dest_path[cp.source_prefix_len] == LDAP_DIRSEP[0] ) ) {
958 		Debug( LDAP_DEBUG_ANY, "homedir: "
959 				"copy_tree: aborting: %s contains %s\n",
960 				source_path, dest_path );
961 		return 1;
962 	}
963 
964 	rc = traverse( source_path, &cb, ctx );
965 
966 	Debug( LDAP_DEBUG_TRACE, "homedir: "
967 			"copy_tree: %s exit %d\n", source_path,
968 			rc );
969 
970 	return rc;
971 }
972 
973 static int
homedir_provision(const char * dest_path,const char * skel_path,uid_t uidn,gid_t gidn,void * ctx)974 homedir_provision(
975 		const char *dest_path,
976 		const char *skel_path,
977 		uid_t uidn,
978 		gid_t gidn,
979 		void *ctx )
980 {
981 	int rc;
982 
983 	assert( dest_path != NULL );
984 
985 	Debug( LDAP_DEBUG_TRACE, "homedir: "
986 			"homedir_provision: %s from skeleton %s\n",
987 			dest_path, skel_path == NULL ? "(none)" : skel_path );
988 	Debug( LDAP_DEBUG_TRACE, "homedir: "
989 			"homedir_provision: %s uidn %ld gidn %ld\n",
990 			dest_path, (long)uidn, (long)gidn );
991 
992 	if ( skel_path == NULL ) {
993 		rc = mkdir( dest_path, 0700 );
994 		if ( rc ) {
995 			int save_errno = errno;
996 			switch ( save_errno ) {
997 				case EEXIST:
998 					/* directory already present; nothing to do */
999 					/* but down chown either */
1000 					rc = 0;
1001 					goto out;
1002 					break;
1003 				default:
1004 					report_errno( "provision_homedir", "mkdir", dest_path );
1005 					goto fail;
1006 			}
1007 		}
1008 		rc = lchown( dest_path, uidn, gidn );
1009 		if ( rc ) {
1010 			report_errno( "provision_homedir", "lchown", dest_path );
1011 			goto fail;
1012 		}
1013 
1014 	} else {
1015 		rc = copy_tree( dest_path, skel_path, uidn, gidn, ctx );
1016 	}
1017 
1018 	goto out;
1019 
1020 fail:
1021 	rc = 1;
1022 	goto out;
1023 out:
1024 	Debug( LDAP_DEBUG_TRACE, "homedir: "
1025 			"homedir_provision: %s to %s exit %d\n",
1026 			skel_path, dest_path, rc );
1027 	return rc;
1028 }
1029 
1030 /* traverse func for rm -rf */
1031 static traverse_cb_ret
traverse_remove_post(void * private,const char * name,const struct stat * st,void * ctx)1032 traverse_remove_post(
1033 		void *private,
1034 		const char *name,
1035 		const struct stat *st,
1036 		void *ctx )
1037 {
1038 	int rc;
1039 
1040 	Debug( LDAP_DEBUG_TRACE, "homedir: "
1041 			"traverse_remove_post: %s entering\n",
1042 			name );
1043 
1044 	if ( (st->st_mode & S_IFMT) == S_IFDIR ) {
1045 		rc = rmdir( name );
1046 		if ( rc != 0 ) {
1047 			report_errno( "traverse_remove_post", "rmdir", name );
1048 			goto fail;
1049 		}
1050 	} else {
1051 		rc = unlink( name );
1052 		if ( rc != 0 ) {
1053 			report_errno( "traverse_remove_post", "unlink", name );
1054 			goto fail;
1055 		}
1056 	}
1057 
1058 	Debug( LDAP_DEBUG_TRACE, "homedir: "
1059 			"traverse_remove_post: %s exit continue\n",
1060 			name );
1061 	return TRAVERSE_CB_CONTINUE;
1062 
1063 fail:
1064 	Debug( LDAP_DEBUG_TRACE, "homedir: "
1065 			"traverse_remove_post: %s exit failure\n",
1066 			name );
1067 	return TRAVERSE_CB_FAIL;
1068 }
1069 
1070 static int
delete_tree(const char * path,void * ctx)1071 delete_tree( const char *path, void *ctx )
1072 {
1073 	const static traverse_cb cb = { NULL, traverse_remove_post, NULL, NULL };
1074 	int rc;
1075 
1076 	assert( path != NULL );
1077 
1078 	Debug( LDAP_DEBUG_TRACE, "homedir: "
1079 			"delete_tree: %s entering\n", path );
1080 
1081 	rc = traverse( path, &cb, ctx );
1082 
1083 	Debug( LDAP_DEBUG_TRACE, "homedir: "
1084 			"delete_tree: %s exit %d\n", path, rc );
1085 
1086 	return rc;
1087 }
1088 
1089 static int
get_tar_name(const char * path,const char * tar_path,char * tar_name,int name_size)1090 get_tar_name(
1091 		const char *path,
1092 		const char *tar_path,
1093 		char *tar_name,
1094 		int name_size )
1095 {
1096 	int rc = 0;
1097 	const char *ch;
1098 	int fd = -1;
1099 	int counter = 0;
1100 	time_t now;
1101 
1102 	assert( path != NULL );
1103 	assert( tar_path != NULL );
1104 	assert( tar_name != NULL );
1105 
1106 	for ( ch = path + strlen( path );
1107 			*ch != LDAP_DIRSEP[0] && ch > path;
1108 			--ch )
1109 		;
1110 	if ( ch <= path || strlen( ch ) < 2 ) {
1111 		Debug( LDAP_DEBUG_ANY, "homedir: "
1112 				"get_tar_name: unable to construct a tar name from input "
1113 				"path \"%s\"\n",
1114 				path );
1115 		goto fail;
1116 	}
1117 	++ch; /* skip past sep */
1118 	time( &now );
1119 
1120 	while ( fd < 0 ) {
1121 		snprintf( tar_name, name_size, "%s" LDAP_DIRSEP "%s-%ld-%d.tar",
1122 				tar_path, ch, (long)now, counter );
1123 		fd = open( tar_name, O_WRONLY|O_CREAT|O_EXCL, 0600 );
1124 		if ( fd < 0 ) {
1125 			int save_errno = errno;
1126 			if ( save_errno != EEXIST ) {
1127 				report_errno( "get_tar_name", "open", tar_name );
1128 				goto fail;
1129 			}
1130 			++counter;
1131 		}
1132 	}
1133 
1134 	rc = 0;
1135 	goto out;
1136 
1137 fail:
1138 	rc = 1;
1139 	*tar_name = '\0';
1140 out:
1141 	if ( fd >= 0 ) close( fd );
1142 	return rc;
1143 }
1144 
1145 /* traverse func for rechown */
1146 static traverse_cb_ret
traverse_chown_pre(void * private,const char * name,const struct stat * st,void * ctx)1147 traverse_chown_pre(
1148 		void *private,
1149 		const char *name,
1150 		const struct stat *st,
1151 		void *ctx )
1152 {
1153 	int rc;
1154 	chown_private *cp = private;
1155 	uid_t set_uidn = -1;
1156 	gid_t set_gidn = -1;
1157 
1158 	assert( private != NULL );
1159 	assert( name != NULL );
1160 	assert( st != NULL );
1161 
1162 	Debug( LDAP_DEBUG_TRACE, "homedir: "
1163 			"traverse_chown_pre: %s entering\n",
1164 			name );
1165 
1166 	if ( st->st_uid == cp->old_uidn ) set_uidn = cp->new_uidn;
1167 	if ( st->st_gid == cp->old_gidn ) set_gidn = cp->new_gidn;
1168 
1169 	if ( set_uidn != (uid_t)-1 || set_gidn != (gid_t)-1 ) {
1170 		rc = lchown( name, set_uidn, set_gidn );
1171 		if ( rc ) {
1172 			report_errno( "traverse_chown_pre", "lchown", name );
1173 			goto fail;
1174 		}
1175 	}
1176 
1177 	Debug( LDAP_DEBUG_TRACE, "homedir: "
1178 			"traverse_chown_pre: %s exit continue\n",
1179 			name );
1180 	return TRAVERSE_CB_CONTINUE;
1181 
1182 fail:
1183 	Debug( LDAP_DEBUG_TRACE, "homedir: "
1184 			"traverse_chown_pre: %s exit failure\n",
1185 			name );
1186 	return TRAVERSE_CB_FAIL;
1187 }
1188 
1189 static int
chown_tree(const char * path,uid_t old_uidn,uid_t new_uidn,gid_t old_gidn,gid_t new_gidn,void * ctx)1190 chown_tree(
1191 		const char *path,
1192 		uid_t old_uidn,
1193 		uid_t new_uidn,
1194 		gid_t old_gidn,
1195 		gid_t new_gidn,
1196 		void *ctx )
1197 {
1198 	traverse_cb cb;
1199 	chown_private cp;
1200 	int rc;
1201 
1202 	assert( path != NULL );
1203 
1204 	Debug( LDAP_DEBUG_TRACE, "homedir: "
1205 			"chown_tree: %s entering\n", path );
1206 
1207 	cb.pre_func = traverse_chown_pre;
1208 	cb.post_func = NULL;
1209 	cb.pre_private = &cp;
1210 	cb.post_private = NULL;
1211 
1212 	cp.old_uidn = old_uidn;
1213 	cp.new_uidn = new_uidn;
1214 	cp.old_gidn = old_gidn;
1215 	cp.new_gidn = new_gidn;
1216 
1217 	rc = traverse( path, &cb, ctx );
1218 
1219 	Debug( LDAP_DEBUG_TRACE, "homedir: "
1220 			"chown_tree: %s exit %d\n", path, rc );
1221 
1222 	return rc;
1223 }
1224 
1225 static int
homedir_rename(const char * source_path,const char * dest_path)1226 homedir_rename( const char *source_path, const char *dest_path )
1227 {
1228 	int rc = 0;
1229 
1230 	assert( source_path != NULL );
1231 	assert( dest_path != NULL );
1232 
1233 	Debug( LDAP_DEBUG_TRACE, "homedir: "
1234 			"homedir_rename: %s to %s\n",
1235 			source_path, dest_path );
1236 	rc = rename( source_path, dest_path );
1237 	if ( rc != 0 ) {
1238 		char ebuf[1024];
1239 		int save_errno = errno;
1240 
1241 		Debug( LDAP_DEBUG_ANY, "homedir: "
1242 				"homedir_rename: rename(\"%s\", \"%s\"): (%s)\n",
1243 				source_path, dest_path,
1244 				AC_STRERROR_R( save_errno, ebuf, sizeof(ebuf) ) );
1245 	}
1246 
1247 	Debug( LDAP_DEBUG_TRACE, "homedir: "
1248 			"homedir_rename: %s to %s exit %d\n",
1249 			source_path, dest_path, rc );
1250 	return rc;
1251 }
1252 
1253 /* FIXME: This assumes ASCII; needs fixing for z/OS */
1254 static int
tar_set_header(ustar_header * tar,const struct stat * st,const char * name)1255 tar_set_header( ustar_header *tar, const struct stat *st, const char *name )
1256 {
1257 	int name_len;
1258 	int rc;
1259 	const char *ch, *end;
1260 
1261 	assert( tar != NULL );
1262 	assert( st != NULL );
1263 	assert( name != NULL );
1264 	assert( sizeof(*tar) == 512 );
1265 	assert( sizeof(tar->name) == 100 );
1266 	assert( sizeof(tar->prefix) == 155 );
1267 	assert( sizeof(tar->checksum) == 8 );
1268 
1269 	memset( tar, 0, sizeof(*tar) );
1270 
1271 	assert( name[0] == LDAP_DIRSEP[0] );
1272 	name += 1; /* skip leading / */
1273 
1274 	name_len = strlen( name );
1275 
1276 	/* fits in tar->name? */
1277 	/* Yes, name and prefix do not need a trailing nul. */
1278 	if ( name_len <= 100 ) {
1279 		strncpy( tar->name, name, 100 );
1280 
1281 		/* try fit in tar->name + tar->prefix */
1282 	} else {
1283 		/* try to find something to stick into tar->name */
1284 		for ( ch = name + name_len - 100, end = name + name_len;
1285 				ch < end && *ch != LDAP_DIRSEP[0];
1286 				++ch )
1287 			;
1288 		if ( end - ch > 0 ) /* +1 skip past sep */
1289 			ch++;
1290 		else {
1291 			/* reset; name too long for UStar */
1292 			Debug( LDAP_DEBUG_ANY, "homedir: "
1293 					"tar_set_header: name too long: \"%s\"\n",
1294 					name );
1295 			ch = name + name_len - 100;
1296 		}
1297 		strncpy( tar->name, ch + 1, 100 );
1298 		{
1299 			int prefix_len = ( ch - 1 ) - name;
1300 			if ( prefix_len > 155 ) prefix_len = 155;
1301 			strncpy( tar->prefix, name, prefix_len );
1302 		}
1303 	}
1304 
1305 	snprintf( tar->mode, 8, "%06lo ", (long)st->st_mode & 07777 );
1306 	snprintf( tar->uid, 8, "%06lo ", (long)st->st_uid );
1307 	snprintf( tar->gid, 8, "%06lo ", (long)st->st_gid );
1308 	snprintf( tar->mtime, 12, "%010lo ", (long)st->st_mtime );
1309 	snprintf( tar->size, 12, "%010lo ", (long)0 );
1310 	switch ( st->st_mode & S_IFMT ) {
1311 		case S_IFREG:
1312 			tar->typeflag[0] = '0';
1313 			snprintf( tar->size, 12, "%010lo ", (long)st->st_size );
1314 			break;
1315 		case S_IFLNK:
1316 			tar->typeflag[0] = '2';
1317 			rc = readlink( name - 1, tar->linkname, 99 );
1318 			if ( rc == -1 ) {
1319 				report_errno( "tar_set_header", "readlink", name );
1320 				goto fail;
1321 			}
1322 			break;
1323 		case S_IFCHR:
1324 			tar->typeflag[0] = '3';
1325 			/* FIXME: this is probably wrong but shouldn't likely be an issue */
1326 			snprintf( tar->devmajor, 8, "%06lo ", (long)st->st_rdev >> 16 );
1327 			snprintf( tar->devminor, 8, "%06lo ", (long)st->st_rdev & 0xffff );
1328 			break;
1329 		case S_IFBLK:
1330 			tar->typeflag[0] = '4';
1331 			/* FIXME: this is probably wrong but shouldn't likely be an issue */
1332 			snprintf( tar->devmajor, 8, "%06lo ", (long)st->st_rdev >> 16 );
1333 			snprintf( tar->devminor, 8, "%06lo ", (long)st->st_rdev & 0xffff );
1334 			break;
1335 		case S_IFDIR:
1336 			tar->typeflag[0] = '5';
1337 			break;
1338 		case S_IFIFO:
1339 			tar->typeflag[0] = '6';
1340 			break;
1341 		default:
1342 			goto fail;
1343 	}
1344 	snprintf( tar->magic, 6, "ustar" );
1345 	tar->version[0] = '0';
1346 	tar->version[1] = '0';
1347 
1348 	{
1349 		unsigned char *uch = (unsigned char *)tar;
1350 		unsigned char *uend = uch + 512;
1351 		unsigned long sum = 0;
1352 
1353 		memset( &tar->checksum, ' ', sizeof(tar->checksum) );
1354 
1355 		for ( ; uch < uend; ++uch )
1356 			sum += *uch;
1357 
1358 		/* zero-padded, six octal digits, followed by NUL then space (!) */
1359 		/* Yes, that's terminated exactly reverse of the others. */
1360 		snprintf( tar->checksum, sizeof(tar->checksum) - 1, "%06lo", sum );
1361 	}
1362 
1363 	return 0;
1364 fail:
1365 	return 1;
1366 }
1367 
1368 static traverse_cb_ret
traverse_tar_pre(void * private,const char * name,const struct stat * st,void * ctx)1369 traverse_tar_pre(
1370 		void *private,
1371 		const char *name,
1372 		const struct stat *st,
1373 		void *ctx )
1374 {
1375 	int rc;
1376 	traverse_cb_ret cbrc;
1377 	tar_private *tp = private;
1378 	ustar_header tar;
1379 	FILE *source = NULL;
1380 
1381 	assert( private != NULL );
1382 	assert( name != NULL );
1383 	assert( st != NULL );
1384 
1385 	Debug( LDAP_DEBUG_TRACE, "homedir: "
1386 			"traverse_tar_pre: %s entering\n", name );
1387 
1388 	switch ( st->st_mode & S_IFMT ) {
1389 		case S_IFREG:
1390 			if ( sizeof(st->st_size) > 4 && ( st->st_size >> 33 ) >= 1 ) {
1391 				Debug( LDAP_DEBUG_TRACE, "homedir: "
1392 						"traverse_tar_pre: %s is larger than 8GiB POSIX UStar "
1393 						"file size limit\n",
1394 						name );
1395 				goto fail;
1396 			}
1397 			/* fallthrough */
1398 		case S_IFDIR:
1399 		case S_IFLNK:
1400 		case S_IFIFO:
1401 		case S_IFCHR:
1402 		case S_IFBLK:
1403 			rc = tar_set_header( &tar, st, name );
1404 			if ( rc ) goto fail;
1405 			break;
1406 		default:
1407 			Debug( LDAP_DEBUG_TRACE, "homedir: "
1408 					"traverse_tar_pre: skipping \"%s\" mode %o\n",
1409 					name, st->st_mode );
1410 			goto done;
1411 	}
1412 
1413 	rc = fwrite( &tar, 1, 512, tp->file );
1414 	if ( rc != 512 ) {
1415 		Debug( LDAP_DEBUG_TRACE, "homedir: "
1416 				"traverse_tar_pre: write error in tar header\n" );
1417 		goto fail;
1418 	}
1419 
1420 	if ( (st->st_mode & S_IFMT) == S_IFREG ) {
1421 		source = fopen( name, "rb" );
1422 		if ( source == NULL ) {
1423 			report_errno( "traverse_tar_pre", "fopen", name );
1424 			goto fail;
1425 		}
1426 		rc = copy_blocks( source, tp->file, name, tp->name );
1427 		if ( rc != 0 ) goto fail;
1428 		fclose( source );
1429 		source = NULL;
1430 	}
1431 
1432 	{ /* advance to end of record */
1433 		off_t pos = ftello( tp->file );
1434 		if ( pos == -1 ) {
1435 			report_errno( "traverse_tar_pre", "ftello", tp->name );
1436 			goto fail;
1437 		}
1438 		pos += ( 512 - ( pos % 512 ) ) % 512;
1439 		rc = fseeko( tp->file, pos, SEEK_SET );
1440 		if ( rc != 0 ) {
1441 			report_errno( "traverse_tar_pre", "fseeko", tp->name );
1442 			goto fail;
1443 		}
1444 	}
1445 
1446 done:
1447 	Debug( LDAP_DEBUG_TRACE, "homedir: "
1448 			"traverse_tar_pre: %s exit continue\n",
1449 			name );
1450 	cbrc = TRAVERSE_CB_CONTINUE;
1451 	goto out;
1452 fail:
1453 	Debug( LDAP_DEBUG_TRACE, "homedir: "
1454 			"traverse_tar_pre: %s exit failure\n",
1455 			name );
1456 	cbrc = TRAVERSE_CB_FAIL;
1457 
1458 out:
1459 	if ( source != NULL ) fclose( source );
1460 	return cbrc;
1461 }
1462 
1463 static int
tar_tree(const char * path,const char * tar_name,void * ctx)1464 tar_tree( const char *path, const char *tar_name, void *ctx )
1465 {
1466 	traverse_cb cb;
1467 	tar_private tp;
1468 	int rc;
1469 
1470 	assert( path != NULL );
1471 	assert( tar_name != NULL );
1472 
1473 	Debug( LDAP_DEBUG_TRACE, "homedir: "
1474 			"tar_tree: %s into %s entering\n", path,
1475 			tar_name );
1476 
1477 	cb.pre_func = traverse_tar_pre;
1478 	cb.post_func = NULL;
1479 	cb.pre_private = &tp;
1480 	cb.post_private = NULL;
1481 
1482 	tp.name = tar_name;
1483 	tp.file = fopen( tar_name, "wb" );
1484 	if ( tp.file == NULL ) {
1485 		report_errno( "tar_tree", "fopen", tar_name );
1486 		goto fail;
1487 	}
1488 
1489 	rc = traverse( path, &cb, ctx );
1490 	if ( rc != 0 ) goto fail;
1491 
1492 	{
1493 		off_t pos = ftello( tp.file );
1494 		if ( pos == -1 ) {
1495 			report_errno( "tar_tree", "ftello", tp.name );
1496 			goto fail;
1497 		}
1498 		pos += 1024; /* two zero records */
1499 		pos += ( 10240 - ( pos % 10240 ) ) % 10240;
1500 		rc = ftruncate( fileno( tp.file ), pos );
1501 		if ( rc != 0 ) {
1502 			report_errno( "tar_tree", "ftrunctate", tp.name );
1503 			goto fail;
1504 		}
1505 	}
1506 
1507 	rc = fclose( tp.file );
1508 	tp.file = NULL;
1509 	if ( rc != 0 ) {
1510 		report_errno( "tar_tree", "fclose", tp.name );
1511 		goto fail;
1512 	}
1513 	goto out;
1514 
1515 fail:
1516 	rc = 1;
1517 out:
1518 	Debug( LDAP_DEBUG_TRACE, "homedir: "
1519 			"tar_tree: %s exit %d\n", path, rc );
1520 	if ( tp.file != NULL ) fclose( tp.file );
1521 	return rc;
1522 }
1523 
1524 static int
homedir_deprovision(const homedir_data * data,const char * path,void * ctx)1525 homedir_deprovision( const homedir_data *data, const char *path, void *ctx )
1526 {
1527 	int rc = 0;
1528 	char tar_name[1024];
1529 
1530 	assert( data != NULL );
1531 	assert( path != NULL );
1532 
1533 	Debug( LDAP_DEBUG_TRACE, "homedir: "
1534 			"homedir_deprovision: %s entering\n",
1535 			path );
1536 
1537 	switch ( data->style ) {
1538 		case DEL_IGNORE:
1539 			Debug( LDAP_DEBUG_TRACE, "homedir: "
1540 					"homedir_deprovision: style is ignore\n" );
1541 			break;
1542 		case DEL_ARCHIVE:
1543 			if ( data->archive_path == NULL ) {
1544 				Debug( LDAP_DEBUG_ANY, "homedir: "
1545 						"homedir_deprovision: archive path not set\n" );
1546 				goto fail;
1547 			}
1548 			rc = get_tar_name( path, data->archive_path, tar_name, 1024 );
1549 			if ( rc != 0 ) goto fail;
1550 			rc = tar_tree( path, tar_name, ctx );
1551 			if ( rc != 0 ) {
1552 				Debug( LDAP_DEBUG_ANY, "homedir: "
1553 						"homedir_deprovision: archive failed, not deleting\n" );
1554 				goto fail;
1555 			}
1556 			/* fall-through */
1557 		case DEL_DELETE:
1558 			rc = delete_tree( path, ctx );
1559 			break;
1560 		default:
1561 			abort();
1562 	}
1563 
1564 	rc = 0;
1565 	goto out;
1566 
1567 fail:
1568 	rc = 1;
1569 out:
1570 	Debug( LDAP_DEBUG_TRACE, "homedir: "
1571 			"homedir_deprovision: %s leaving\n",
1572 			path );
1573 
1574 	return rc;
1575 }
1576 
1577 /* FIXME: This assumes ASCII; needs fixing for z/OS */
1578 /* FIXME: This should also be in a slapd library function somewhere */
1579 #define MAX_MATCHES ( 10 )
1580 static int
homedir_match(const homedir_regexp * r,const char * homedir,char * result,size_t result_size)1581 homedir_match(
1582 		const homedir_regexp *r,
1583 		const char *homedir,
1584 		char *result,
1585 		size_t result_size )
1586 {
1587 	int rc;
1588 	int n;
1589 	regmatch_t matches[MAX_MATCHES];
1590 	char *resc, *repc;
1591 
1592 	assert( r != NULL );
1593 	assert( homedir != NULL );
1594 	assert( result_size > 1 );
1595 
1596 	memset( matches, 0, sizeof(matches) );
1597 	rc = regexec( &r->compiled, homedir, MAX_MATCHES, matches, 0 );
1598 	if ( rc ) {
1599 		if ( rc != REG_NOMATCH ) {
1600 			char msg[256];
1601 			regerror( rc, &r->compiled, msg, sizeof(msg) );
1602 			Debug( LDAP_DEBUG_ANY, "homedir_match: "
1603 					"%s\n", msg );
1604 		}
1605 		return rc;
1606 	}
1607 
1608 	for ( resc = result, repc = r->replace;
1609 			result_size > 1 && *repc != '\0';
1610 			++repc, ++resc, --result_size ) {
1611 		switch ( *repc ) {
1612 			case '$':
1613 				++repc;
1614 				n = ( *repc ) - '0';
1615 				if ( n < 0 || n > ( MAX_MATCHES - 1 ) ||
1616 						matches[n].rm_so < 0 ) {
1617 					Debug( LDAP_DEBUG_ANY, "homedir: "
1618 							"invalid regex term expansion in \"%s\" "
1619 							"at char %ld, n is %d\n",
1620 							r->replace, (long)( repc - r->replace ), n );
1621 					return 1;
1622 				}
1623 				{
1624 					size_t match_len = matches[n].rm_eo - matches[n].rm_so;
1625 					const char *match_start = homedir + matches[n].rm_so;
1626 					if ( match_len >= result_size ) goto too_long;
1627 
1628 					memcpy( resc, match_start, match_len );
1629 					result_size -= match_len;
1630 					resc += match_len - 1;
1631 				}
1632 				break;
1633 
1634 			case '\\':
1635 				++repc;
1636 				/* fallthrough */
1637 
1638 			default:
1639 				*resc = *repc;
1640 		}
1641 	}
1642 	*resc = '\0';
1643 	if ( *repc != '\0' ) goto too_long;
1644 
1645 	return 0;
1646 
1647 too_long:
1648 	Debug( LDAP_DEBUG_ANY, "homedir: "
1649 			"regex expansion of %s too long\n",
1650 			r->replace );
1651 	*result = '\0';
1652 	return 1;
1653 }
1654 
1655 /* Sift through an entry for interesting values
1656  * return 0 on success and set vars
1657  * return 1 if homedir is not present or not valid
1658  * sets presence if any homedir attributes are noticed
1659  */
1660 static int
harvest_values(const homedir_data * data,const Entry * e,char * home_buf,int home_buf_size,uid_t * uidn,gid_t * gidn,int * presence)1661 harvest_values(
1662 		const homedir_data *data,
1663 		const Entry *e,
1664 		char *home_buf,
1665 		int home_buf_size,
1666 		uid_t *uidn,
1667 		gid_t *gidn,
1668 		int *presence )
1669 {
1670 	Attribute *a;
1671 	char *homedir = NULL;
1672 
1673 	assert( data != NULL );
1674 	assert( e != NULL );
1675 	assert( home_buf != NULL );
1676 	assert( home_buf_size > 1 );
1677 	assert( uidn != NULL );
1678 	assert( gidn != NULL );
1679 	assert( presence != NULL );
1680 
1681 	*presence = 0;
1682 	if ( e == NULL ) return 1;
1683 	*uidn = 0;
1684 	*gidn = 0;
1685 
1686 	for ( a = e->e_attrs; a->a_next != NULL; a = a->a_next ) {
1687 		if ( a->a_desc == data->home_ad ) {
1688 			homedir = a->a_vals[0].bv_val;
1689 			*presence = 1;
1690 		} else if ( a->a_desc == data->uidn_ad ) {
1691 			*uidn = (uid_t)strtol( a->a_vals[0].bv_val, NULL, 10 );
1692 			*presence = 1;
1693 		} else if ( a->a_desc == data->gidn_ad ) {
1694 			*gidn = (gid_t)strtol( a->a_vals[0].bv_val, NULL, 10 );
1695 			*presence = 1;
1696 		}
1697 	}
1698 	if ( homedir != NULL ) {
1699 		homedir_regexp *r;
1700 
1701 		for ( r = data->regexps; r != NULL; r = r->next ) {
1702 			int rc = homedir_match( r, homedir, home_buf, home_buf_size );
1703 			if ( rc == 0 ) return 0;
1704 		}
1705 	}
1706 
1707 	return 1;
1708 }
1709 
1710 static int
homedir_mod_cleanup(Operation * op,SlapReply * rs)1711 homedir_mod_cleanup( Operation *op, SlapReply *rs )
1712 {
1713 	slap_callback *cb = NULL;
1714 	slap_callback **cbp = NULL;
1715 	homedir_cb_data *cb_data = NULL;
1716 	Entry *e = NULL;
1717 
1718 	Debug( LDAP_DEBUG_TRACE, "homedir: "
1719 			"homedir_mod_cleanup: entering\n" );
1720 
1721 	for ( cbp = &op->o_callback;
1722 			*cbp != NULL && (*cbp)->sc_cleanup != homedir_mod_cleanup;
1723 			cbp = &(*cbp)->sc_next )
1724 		;
1725 
1726 	if ( *cbp == NULL ) goto out;
1727 	cb = *cbp;
1728 
1729 	cb_data = (homedir_cb_data *)cb->sc_private;
1730 	e = cb_data->entry;
1731 
1732 	Debug( LDAP_DEBUG_TRACE, "homedir: "
1733 			"homedir_mod_cleanup: found <%s>\n",
1734 			e->e_nname.bv_val );
1735 	entry_free( e );
1736 	op->o_tmpfree( cb_data, op->o_tmpmemctx );
1737 	*cbp = cb->sc_next;
1738 	op->o_tmpfree( cb, op->o_tmpmemctx );
1739 
1740 out:
1741 
1742 	Debug( LDAP_DEBUG_TRACE, "homedir: "
1743 			"homedir_mod_cleanup: leaving\n" );
1744 	return SLAP_CB_CONTINUE;
1745 }
1746 
1747 static int
homedir_mod_response(Operation * op,SlapReply * rs)1748 homedir_mod_response( Operation *op, SlapReply *rs )
1749 {
1750 	slap_overinst *on = NULL;
1751 	homedir_data *data = NULL;
1752 	slap_callback *cb = NULL;
1753 	homedir_cb_data *cb_data = NULL;
1754 	Entry *e = NULL;
1755 	int rc = SLAP_CB_CONTINUE;
1756 
1757 	Debug( LDAP_DEBUG_TRACE, "homedir: "
1758 			"homedir_mod_response: entering\n" );
1759 
1760 	if ( rs->sr_err != LDAP_SUCCESS ) {
1761 		Debug( LDAP_DEBUG_TRACE, "homedir: "
1762 				"homedir_mod_response: op was not successful\n" );
1763 		goto out;
1764 	}
1765 
1766 	/* Retrieve stashed entry */
1767 	for ( cb = op->o_callback;
1768 			cb != NULL && cb->sc_cleanup != homedir_mod_cleanup;
1769 			cb = cb->sc_next )
1770 		;
1771 	if ( cb == NULL ) goto out;
1772 	cb_data = (homedir_cb_data *)cb->sc_private;
1773 	e = cb_data->entry;
1774 	on = cb_data->on;
1775 	data = on->on_bi.bi_private;
1776 	assert( e != NULL );
1777 	assert( data != NULL );
1778 	Debug( LDAP_DEBUG_TRACE, "homedir: "
1779 			"homedir_mod_response: found <%s>\n",
1780 			e->e_nname.bv_val );
1781 
1782 	switch ( op->o_tag ) {
1783 		case LDAP_REQ_DELETE: {
1784 			char home_buf[1024];
1785 			uid_t uidn = 0;
1786 			gid_t gidn = 0;
1787 			int presence;
1788 
1789 			Debug( LDAP_DEBUG_TRACE, "homedir: "
1790 					"homedir_mod_response: successful delete found\n" );
1791 			rc = harvest_values( data, e, home_buf, sizeof(home_buf), &uidn,
1792 					&gidn, &presence );
1793 			if ( rc == 0 && uidn >= data->min_uid ) {
1794 				homedir_deprovision( data, home_buf, op->o_tmpmemctx );
1795 			} else {
1796 				Debug( LDAP_DEBUG_TRACE, "homedir: "
1797 						"homedir_mod_response: skipping\n" );
1798 			}
1799 			rc = SLAP_CB_CONTINUE;
1800 			break;
1801 		}
1802 
1803 		case LDAP_REQ_MODIFY:
1804 		case LDAP_REQ_MODRDN: {
1805 			Operation nop = *op;
1806 			Entry *old_entry = e;
1807 			Entry *new_entry = NULL;
1808 			Entry *etmp;
1809 			char old_home[1024];
1810 			char new_home[1024];
1811 			uid_t old_uidn, new_uidn;
1812 			uid_t old_gidn, new_gidn;
1813 			int old_valid = 0;
1814 			int new_valid = 0;
1815 			int old_presence, new_presence;
1816 
1817 			Debug( LDAP_DEBUG_TRACE, "homedir: "
1818 					"homedir_mod_response: successful modify/modrdn found\n" );
1819 
1820 			/* retrieve the revised entry */
1821 			nop.o_bd = on->on_info->oi_origdb;
1822 			rc = overlay_entry_get_ov(
1823 					&nop, &op->o_req_ndn, NULL, NULL, 0, &etmp, on );
1824 			if ( etmp != NULL ) {
1825 				new_entry = entry_dup( etmp );
1826 				overlay_entry_release_ov( &nop, etmp, 0, on );
1827 			}
1828 			if ( rc || new_entry == NULL ) {
1829 				Debug( LDAP_DEBUG_ANY, "homedir: "
1830 						"homedir_mod_response: unable to get revised <%s>\n",
1831 						op->o_req_ndn.bv_val );
1832 				if ( new_entry != NULL ) {
1833 					entry_free( new_entry );
1834 					new_entry = NULL;
1835 				}
1836 			}
1837 
1838 			/* analyze old and new */
1839 			rc = harvest_values( data, old_entry, old_home, 1024, &old_uidn,
1840 					&old_gidn, &old_presence );
1841 			if ( rc == 0 && old_uidn >= data->min_uid ) old_valid = 1;
1842 			if ( new_entry != NULL ) {
1843 				rc = harvest_values( data, new_entry, new_home, 1024, &new_uidn,
1844 						&new_gidn, &new_presence );
1845 				if ( rc == 0 && new_uidn >= data->min_uid ) new_valid = 1;
1846 				entry_free( new_entry );
1847 				new_entry = NULL;
1848 			}
1849 
1850 			if ( new_valid && !old_valid ) { /* like an add */
1851 				if ( old_presence )
1852 					Debug( LDAP_DEBUG_TRACE, "homedir: "
1853 							"homedir_mod_response: old entry is now valid\n" );
1854 				Debug( LDAP_DEBUG_TRACE, "homedir: "
1855 						"homedir_mod_response: treating like an add\n" );
1856 				homedir_provision( new_home, data->skeleton_path, new_uidn,
1857 						new_gidn, op->o_tmpmemctx );
1858 
1859 			} else if ( old_valid && !new_valid &&
1860 					!new_presence ) { /* like a del */
1861 				Debug( LDAP_DEBUG_TRACE, "homedir: "
1862 						"homedir_mod_response: treating like a del\n" );
1863 				homedir_deprovision( data, old_home, op->o_tmpmemctx );
1864 
1865 			} else if ( new_valid && old_valid ) { /* change */
1866 				int did_something = 0;
1867 
1868 				if ( strcmp( old_home, new_home ) != 0 ) {
1869 					Debug( LDAP_DEBUG_TRACE, "homedir: "
1870 							"homedir_mod_response: treating like a rename\n" );
1871 					homedir_rename( old_home, new_home );
1872 					did_something = 1;
1873 				}
1874 				if ( old_uidn != new_uidn || old_gidn != new_gidn ) {
1875 					Debug( LDAP_DEBUG_ANY, "homedir: "
1876 							"homedir_mod_response: rechowning\n" );
1877 					chown_tree( new_home, old_uidn, new_uidn, old_gidn,
1878 							new_gidn, op->o_tmpmemctx );
1879 					did_something = 1;
1880 				}
1881 				if ( !did_something ) {
1882 					Debug( LDAP_DEBUG_TRACE, "homedir: "
1883 							"homedir_mod_response: nothing to do\n" );
1884 				}
1885 			} else if ( old_presence || new_presence ) {
1886 				Debug( LDAP_DEBUG_ANY, "homedir: "
1887 						"homedir_mod_response: <%s> values present "
1888 						"but invalid; ignoring\n",
1889 						op->o_req_ndn.bv_val );
1890 			}
1891 			rc = SLAP_CB_CONTINUE;
1892 			break;
1893 		}
1894 
1895 		default:
1896 			rc = SLAP_CB_CONTINUE;
1897 	}
1898 
1899 out:
1900 	Debug( LDAP_DEBUG_TRACE, "homedir: "
1901 			"homedir_mod_response: leaving\n" );
1902 	return rc;
1903 }
1904 
1905 static int
homedir_op_mod(Operation * op,SlapReply * rs)1906 homedir_op_mod( Operation *op, SlapReply *rs )
1907 {
1908 	slap_overinst *on = (slap_overinst *)op->o_bd->bd_info;
1909 	slap_callback *cb = NULL;
1910 	homedir_cb_data *cb_data = NULL;
1911 	Entry *e = NULL;
1912 	Entry *se = NULL;
1913 	Operation nop = *op;
1914 	int rc;
1915 
1916 	Debug( LDAP_DEBUG_TRACE, "homedir: "
1917 			"homedir_op_mod: entering\n" );
1918 
1919 	/* retrieve the entry */
1920 	nop.o_bd = on->on_info->oi_origdb;
1921 	rc = overlay_entry_get_ov( &nop, &op->o_req_ndn, NULL, NULL, 0, &e, on );
1922 	if ( e != NULL ) {
1923 		se = entry_dup( e );
1924 		overlay_entry_release_ov( &nop, e, 0, on );
1925 		e = se;
1926 	}
1927 	if ( rc || e == NULL ) {
1928 		Debug( LDAP_DEBUG_ANY, "homedir: "
1929 				"homedir_op_mod: unable to get <%s>\n",
1930 				op->o_req_ndn.bv_val );
1931 		goto out;
1932 	}
1933 
1934 	/* Allocate the callback to hold the entry */
1935 	cb = op->o_tmpalloc( sizeof(slap_callback), op->o_tmpmemctx );
1936 	cb_data = op->o_tmpalloc( sizeof(homedir_cb_data), op->o_tmpmemctx );
1937 	cb->sc_cleanup = homedir_mod_cleanup;
1938 	cb->sc_response = homedir_mod_response;
1939 	cb->sc_private = cb_data;
1940 	cb_data->entry = e;
1941 	e = NULL;
1942 	cb_data->on = on;
1943 	cb->sc_next = op->o_callback;
1944 	op->o_callback = cb;
1945 
1946 out:
1947 	if ( e != NULL ) entry_free( e );
1948 	Debug( LDAP_DEBUG_TRACE, "homedir: "
1949 			"homedir_op_mod: leaving\n" );
1950 	return SLAP_CB_CONTINUE;
1951 }
1952 
1953 static int
homedir_response(Operation * op,SlapReply * rs)1954 homedir_response( Operation *op, SlapReply *rs )
1955 {
1956 	slap_overinst *on = (slap_overinst *)op->o_bd->bd_info;
1957 	homedir_data *data = on->on_bi.bi_private;
1958 
1959 	Debug( LDAP_DEBUG_TRACE, "homedir: "
1960 			"homedir_response: entering\n" );
1961 	if ( rs->sr_err != LDAP_SUCCESS || data == NULL ) return SLAP_CB_CONTINUE;
1962 
1963 	switch ( op->o_tag ) {
1964 		case LDAP_REQ_ADD: { /* Check for new homedir */
1965 			char home_buf[1024];
1966 			uid_t uidn = 0;
1967 			gid_t gidn = 0;
1968 			int rc, presence;
1969 
1970 			rc = harvest_values( data, op->ora_e, home_buf, sizeof(home_buf),
1971 					&uidn, &gidn, &presence );
1972 			if ( rc == 0 && uidn >= data->min_uid ) {
1973 				homedir_provision( home_buf, data->skeleton_path, uidn, gidn,
1974 						op->o_tmpmemctx );
1975 			}
1976 			return SLAP_CB_CONTINUE;
1977 		}
1978 
1979 		default:
1980 			return SLAP_CB_CONTINUE;
1981 	}
1982 
1983 	return SLAP_CB_CONTINUE;
1984 }
1985 
1986 static int
homedir_db_init(BackendDB * be,ConfigReply * cr)1987 homedir_db_init( BackendDB *be, ConfigReply *cr )
1988 {
1989 	slap_overinst *on = (slap_overinst *)be->bd_info;
1990 	homedir_data *data = ch_calloc( 1, sizeof(homedir_data) );
1991 	const char *text;
1992 
1993 	if ( slap_str2ad( "homeDirectory", &data->home_ad, &text ) ||
1994 			slap_str2ad( "uidNumber", &data->uidn_ad, &text ) ||
1995 			slap_str2ad( "gidNumber", &data->gidn_ad, &text ) ) {
1996 		Debug( LDAP_DEBUG_ANY, "homedir: "
1997 				"nis schema not available\n" );
1998 		return 1;
1999 	}
2000 
2001 	data->skeleton_path = strdup( DEFAULT_SKEL );
2002 	data->min_uid = DEFAULT_MIN_UID;
2003 	data->archive_path = NULL;
2004 
2005 	on->on_bi.bi_private = data;
2006 	return 0;
2007 }
2008 
2009 static int
homedir_db_destroy(BackendDB * be,ConfigReply * cr)2010 homedir_db_destroy( BackendDB *be, ConfigReply *cr )
2011 {
2012 	slap_overinst *on = (slap_overinst *)be->bd_info;
2013 	homedir_data *data = on->on_bi.bi_private;
2014 	homedir_regexp *r, *rnext;
2015 
2016 	if ( data != NULL ) {
2017 		for ( r = data->regexps; r != NULL; r = rnext ) {
2018 			rnext = r->next;
2019 			ch_free( r->match );
2020 			ch_free( r->replace );
2021 			regfree( &r->compiled );
2022 			ch_free( r );
2023 		}
2024 		data->regexps = NULL;
2025 		if ( data->skeleton_path != NULL ) ch_free( data->skeleton_path );
2026 		if ( data->archive_path != NULL ) ch_free( data->archive_path );
2027 		ch_free( data );
2028 	}
2029 
2030 	return 0;
2031 }
2032 
2033 int
homedir_initialize()2034 homedir_initialize()
2035 {
2036 	int rc;
2037 
2038 	assert( ' ' == 32 ); /* Lots of ASCII requirements for now */
2039 
2040 	memset( &homedir, 0, sizeof(homedir) );
2041 
2042 	homedir.on_bi.bi_type = "homedir";
2043 	homedir.on_bi.bi_db_init = homedir_db_init;
2044 	homedir.on_bi.bi_db_destroy = homedir_db_destroy;
2045 	homedir.on_bi.bi_op_delete = homedir_op_mod;
2046 	homedir.on_bi.bi_op_modify = homedir_op_mod;
2047 	homedir.on_response = homedir_response;
2048 
2049 	homedir.on_bi.bi_cf_ocs = homedirocs;
2050 	rc = config_register_schema( homedircfg, homedirocs );
2051 	if ( rc ) return rc;
2052 
2053 	ldap_pvt_thread_mutex_init( &readdir_mutex );
2054 
2055 	return overlay_register( &homedir );
2056 }
2057 
2058 int
homedir_terminate()2059 homedir_terminate()
2060 {
2061 	ldap_pvt_thread_mutex_destroy( &readdir_mutex );
2062 	return 0;
2063 }
2064 
2065 #if SLAPD_OVER_HOMEDIR == SLAPD_MOD_DYNAMIC && defined(PIC)
2066 int
init_module(int argc,char * argv[])2067 init_module( int argc, char *argv[] )
2068 {
2069 	return homedir_initialize();
2070 }
2071 
2072 int
term_module()2073 term_module()
2074 {
2075 	return homedir_terminate();
2076 }
2077 #endif
2078 
2079 #endif /* SLAPD_OVER_HOMEDIR */
2080