1 /*-
2  * Copyright (c) 1980, 1987, 1988, 1991, 1993, 1994
3  *	The Regents of the University of California.  All rights reserved.
4  * Copyright (c) 2001 Mark R V Murray
5  * All rights reserved.
6  * Copyright (c) 2001 Networks Associates Technology, Inc.
7  * All rights reserved.
8  * Copyright (c) 2004 Joe R. Doupnik
9  * All rights reserved.
10  * Copyright (c) 2005 Martin Mersberger
11  * All rights reserved.
12  *
13  * Portions of this software were developed for the FreeBSD Project by
14  * ThinkSec AS and NAI Labs, the Security Research Division of Network
15  * Associates, Inc.  under DARPA/SPAWAR contract N66001-01-C-8035
16  * ("CBOSS"), as part of the DARPA CHATS research program.
17  *
18  * Redistribution and use in source and binary forms, with or without
19  * modification, are permitted provided that the following conditions
20  * are met:
21  * 1. Redistributions of source code must retain the above copyright
22  *    notice, this list of conditions and the following disclaimer.
23  * 2. Redistributions in binary form must reproduce the above copyright
24  *    notice, this list of conditions and the following disclaimer in the
25  *    documentation and/or other materials provided with the distribution.
26  * 3. The name of the author may not be used to endorse or promote
27  *    products derived from this software without specific prior written
28  *    permission.
29  * 4. Neither the name of the University nor the names of its contributors
30  *    may be used to endorse or promote products derived from this software
31  *    without specific prior written permission.
32  *
33  * THIS SOFTWARE IS PROVIDED BY THE AUTHOR AND CONTRIBUTORS ``AS IS'' AND
34  * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
35  * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
36  * ARE DISCLAIMED.  IN NO EVENT SHALL THE AUTHOR OR CONTRIBUTORS BE LIABLE
37  * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
38  * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS
39  * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION)
40  * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT
41  * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY
42  * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF
43  * SUCH DAMAGE.
44  */
45 
46 #include <sys/cdefs.h>
47 #include <sys/param.h>
48 #include <sys/stat.h>
49 #include <sys/types.h>
50 
51 #include <fcntl.h>
52 #include <libutil.h>
53 #include <paths.h>
54 #include <pwd.h>
55 #include <stdio.h>
56 #include <stdlib.h>
57 #include <string.h>
58 #include <syslog.h>
59 #include <time.h>
60 #include <unistd.h>
61 #include <err.h>
62 #include <errno.h>
63 #include <dirent.h>
64 
65 #define PAM_SM_SESSION
66 
67 #include <security/pam_appl.h>
68 #include <security/pam_modules.h>
69 #include <security/pam_mod_misc.h>
70 
71 void
copymkdir(char const * dir,char const * skel,mode_t mode,uid_t uid,gid_t gid)72 copymkdir(char const * dir, char const * skel, mode_t mode, uid_t uid, gid_t gid)
73 {
74 	int             rc = 0;
75 	char            src[MAXPATHLEN];
76 	char            dst[MAXPATHLEN];
77 
78 	if (mkdir(dir, mode) != 0 && errno != EEXIST) {
79 		PAM_LOG("mkdir(%s)", dir);
80 	} else {
81 		int             infd, outfd;
82 		struct stat     st;
83 
84 		static char     counter = 0;
85 		static char    *copybuf = NULL;
86 
87 		++counter;
88 		chown(dir, uid, gid);
89 		if (skel == NULL || *skel == '\0')
90 			rc = 1;
91 		else {
92 			DIR            *d = opendir(skel);
93 
94 			if (d != NULL) {
95 				struct dirent  *e;
96 
97 				while ((e = readdir(d)) != NULL) {
98 					char           *p = e->d_name;
99 
100 					if (snprintf(src, sizeof(src), "%s/%s", skel, p) >= (int)sizeof(src))
101 						PAM_LOG("warning: pathname too long '%s/%s' (skel not copied)", skel, p);
102 					else if (stat(src, &st) == 0) {
103 						if (strncmp(p, "dot.", 4) == 0)	/* Conversion */
104 							p += 3;
105 						if (snprintf(dst, sizeof(dst), "%s/%s", dir, p) >= (int)sizeof(dst))
106 							PAM_LOG("warning: path too long '%s/%s' (skel file skipped)", dir, p);
107 						else {
108 						    if (S_ISDIR(st.st_mode)) {	/* Recurse for this */
109 							if (strcmp(e->d_name, ".") != 0 && strcmp(e->d_name, "..") != 0)
110 								copymkdir(dst, src, (st.st_mode & 0777), uid, gid);
111 								chflags(dst, st.st_flags);	/* propogate flags */
112 							/*
113 							 * Note: don't propogate special attributes
114 							 * but do propogate file flags
115 							 */
116 						    } else if (S_ISREG(st.st_mode) && (outfd = open(dst, O_RDWR | O_CREAT | O_EXCL, st.st_mode)) != -1) {
117 							if ((infd = open(src, O_RDONLY)) == -1) {
118 								close(outfd);
119 								remove(dst);
120 							} else {
121 								int             b;
122 
123 								/*
124 								 * Allocate our copy buffer if we need to
125 								 */
126 								if (copybuf == NULL)
127 									copybuf = malloc(4096);
128 								while ((b = read(infd, copybuf, 4096)) > 0)
129 									write(outfd, copybuf, b);
130 								close(infd);
131 								/*
132 								 * Propogate special filesystem flags
133 								 */
134 								fchown(outfd, uid, gid);
135 								fchflags(outfd, st.st_flags);
136 								close(outfd);
137 								chown(dst, uid, gid);
138 							}
139 						    }
140 						}
141 					}
142 				}
143 				closedir(d);
144 			}
145 		}
146 		if (--counter == 0 && copybuf != NULL) {
147 			free(copybuf);
148 			copybuf = NULL;
149 		}
150 	}
151 }
152 
153 
154 PAM_EXTERN int
pam_sm_open_session(pam_handle_t * pamh,int flags __unused,int argc,const char * argv[])155 pam_sm_open_session(pam_handle_t *pamh, int flags __unused,
156     int argc , const char *argv[] )
157 {
158 	struct passwd *pwd;
159 	const char *user;
160 	int pam_err;
161 	struct stat st;
162 	mode_t *set;
163 	set=NULL;
164 	int skel=0; int modef=0; int i;
165 	char *res=NULL;
166 	char buff[MAXPATHLEN]; char skeldir[MAXPATHLEN]; char modeval[5];
167 
168 	pam_err = pam_get_user(pamh, &user, NULL);
169 	if (pam_err != PAM_SUCCESS)
170 		return (pam_err);
171 	if (user == NULL || (pwd = getpwnam(user)) == NULL)
172 		return (PAM_SERVICE_ERR);
173 	if ((stat(pwd->pw_dir,&st) == 0) && ( S_ISDIR(st.st_mode))) {
174 		return (PAM_SUCCESS);
175 	}
176 
177 	// default values
178 	strncpy(skeldir,"/usr/share/skel",sizeof(skeldir));
179 	strncpy(modeval,"0755",sizeof(modeval));
180 
181 	for (i=0;i<argc;i++) {
182 		strncpy(buff,argv[i],sizeof(buff));
183 		res=strtok(buff,"=");
184 		while (res != NULL ) {
185 			if (skel==1 ) {
186 				if ((stat(res,&st) != 0  ) || ( !S_ISDIR(st.st_mode)) )  {
187 					pam_err=PAM_SESSION_ERR;
188 					PAM_LOG("Skel directory %s does not exist",res);
189 					goto err;
190 				}
191 				strncpy(skeldir,res,sizeof(skeldir));
192 				skel=0;
193 			}
194 			else if (strncmp(res,"skel",4) == 0 ) {
195 				skel=1;
196 			}
197 			else if (modef==1) {
198 				strncpy(modeval,res,sizeof(modeval));
199 				modef=0;
200 			}
201 			else if (strncmp(res,"mode",5) == 0 ) {
202 				modef=1;
203 			}
204 			res=strtok(NULL,"=");
205 		}
206 	}
207 
208 	if (( set=setmode(modeval) ) == NULL ) {
209 		pam_err=PAM_SESSION_ERR;
210 		PAM_LOG("Value set in mode is not a mode - see chmod(1) for details");
211 		goto err;
212 	}
213 
214 	copymkdir(pwd->pw_dir, skeldir, getmode(set, S_IRWXU | S_IRWXG | S_IRWXO), pwd->pw_uid,pwd->pw_gid);
215 	free(set);
216 	return (PAM_SUCCESS);
217 
218 err:
219 	if (openpam_get_option(pamh, "no_fail"))
220 		return (PAM_SUCCESS);
221 	return (pam_err);
222 }
223 
224 PAM_EXTERN int
pam_sm_close_session(pam_handle_t * pamh __unused,int flags __unused,int argc __unused,const char * argv[]__unused)225 pam_sm_close_session(pam_handle_t *pamh __unused, int flags __unused,
226 	int argc __unused, const char *argv[] __unused)
227 {
228 	return (PAM_SUCCESS);
229 }
230 
231 PAM_MODULE_ENTRY("pam_mkhomedir");
232