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