1/*
2 * Copyright (c) 1989 Regents of the University of California.
3 * All rights reserved.  The Berkeley software License Agreement
4 * specifies the terms and conditions for redistribution.
5 */
6
7#include <popper.h>
8RCSID("$Id$");
9
10/*
11 * Run as the user in `pwd'
12 */
13
14int
15changeuser(POP *p, struct passwd *pwd)
16{
17    if(setgid(pwd->pw_gid) < 0) {
18	pop_log (p, POP_PRIORITY,
19		 "Unable to change to gid %u: %s",
20		 (unsigned)pwd->pw_gid,
21		 strerror(errno));
22	return pop_msg (p, POP_FAILURE,
23			"Unable to change gid");
24    }
25    if(setuid(pwd->pw_uid) < 0) {
26	pop_log (p, POP_PRIORITY,
27		 "Unable to change to uid %u: %s",
28		 (unsigned)pwd->pw_uid,
29		 strerror(errno));
30	return pop_msg (p, POP_FAILURE,
31			"Unable to change uid");
32    }
33#ifdef DEBUG
34    if(p->debug)
35	pop_log(p, POP_DEBUG,"uid = %u, gid = %u",
36	       (unsigned)getuid(),
37	       (unsigned)getgid());
38#endif /* DEBUG */
39    return POP_SUCCESS;
40}
41
42/*
43 *  dropcopy:   Make a temporary copy of the user's mail drop and
44 *  save a stream pointer for it.
45 */
46
47int
48pop_dropcopy(POP *p, struct passwd *pwp)
49{
50    int                     mfd;                    /*  File descriptor for
51                                                        the user's maildrop */
52    int                     dfd;                    /*  File descriptor for
53                                                        the SERVER maildrop */
54    FILE		    *tf;		    /*  The temp file */
55    char		    template[POP_TMPSIZE];  /*  Temp name holder */
56    char                    buffer[BUFSIZ];         /*  Read buffer */
57    long                    offset;                 /*  Old/New boundary */
58    int                     nchar;                  /*  Bytes written/read */
59    int                     tf_fd;                  /*  fd for temp file */
60    int			    ret;
61
62    /*  Create a temporary maildrop into which to copy the updated maildrop */
63    snprintf(p->temp_drop, sizeof(p->temp_drop), POP_DROP,p->user);
64
65#ifdef DEBUG
66    if(p->debug)
67        pop_log(p,POP_DEBUG,"Creating temporary maildrop '%s'",
68            p->temp_drop);
69#endif /* DEBUG */
70
71    /* Here we work to make sure the user doesn't cause us to remove or
72     * write over existing files by limiting how much work we do while
73     * running as root.
74     */
75
76    strlcpy(template, POP_TMPDROP, sizeof(template));
77    if ((tf_fd = mkstemp(template)) < 0 ||
78	(tf = fdopen(tf_fd, "w+")) == NULL) {
79        pop_log(p,POP_PRIORITY,
80            "Unable to create temporary temporary maildrop '%s': %s",template,
81		strerror(errno));
82        return pop_msg(p,POP_FAILURE,
83		"System error, can't create temporary file.");
84    }
85
86    /* Now give this file to the user	*/
87    chown(template, pwp->pw_uid, pwp->pw_gid);
88    chmod(template, 0600);
89
90    /* Now link this file to the temporary maildrop.  If this fails it
91     * is probably because the temporary maildrop already exists.  If so,
92     * this is ok.  We can just go on our way, because by the time we try
93     * to write into the file we will be running as the user.
94     */
95    link(template,p->temp_drop);
96    fclose(tf);
97    unlink(template);
98
99    ret = changeuser(p, pwp);
100    if (ret != POP_SUCCESS)
101	return ret;
102
103    /* Open for append,  this solves the crash recovery problem */
104    if ((dfd = open(p->temp_drop,O_RDWR|O_APPEND|O_CREAT,0600)) == -1){
105        pop_log(p,POP_PRIORITY,
106            "Unable to open temporary maildrop '%s': %s",p->temp_drop,
107		strerror(errno));
108        return pop_msg(p,POP_FAILURE,
109		"System error, can't open temporary file, do you own it?");
110    }
111
112    /*  Lock the temporary maildrop */
113    if ( flock (dfd, (LOCK_EX | LOCK_NB)) == -1 )
114    switch(errno) {
115        case EWOULDBLOCK:
116            return pop_msg(p,POP_FAILURE,
117                 "%sMaildrop lock busy!  Is another session active?",
118			   (p->flags & POP_FLAG_CAPA) ? "[IN-USE] " : "");
119            /* NOTREACHED */
120        default:
121            return pop_msg(p,POP_FAILURE,"flock: '%s': %s", p->temp_drop,
122		strerror(errno));
123            /* NOTREACHED */
124        }
125
126    /* May have grown or shrunk between open and lock! */
127    offset = lseek(dfd,0, SEEK_END);
128
129    /*  Open the user's maildrop, If this fails,  no harm in assuming empty */
130    if ((mfd = open(p->drop_name,O_RDWR)) > 0) {
131
132        /*  Lock the maildrop */
133        if (flock (mfd, LOCK_EX) == -1) {
134            close(mfd) ;
135            return pop_msg(p,POP_FAILURE, "flock: '%s': %s", p->temp_drop,
136		strerror(errno));
137        }
138
139        /*  Copy the actual mail drop into the temporary mail drop */
140        while ( (nchar=read(mfd,buffer,BUFSIZ)) > 0 )
141            if ( nchar != write(dfd,buffer,nchar) ) {
142                nchar = -1 ;
143                break ;
144            }
145
146        if ( nchar != 0 ) {
147            /* Error adding new mail.  Truncate to original size,
148               and leave the maildrop as is.  The user will not
149               see the new mail until the error goes away.
150               Should let them process the current backlog,  in case
151               the error is a quota problem requiring deletions! */
152            ftruncate(dfd,(int)offset) ;
153        } else {
154            /* Mail transferred!  Zero the mail drop NOW,  that we
155               do not have to do gymnastics to figure out what's new
156               and what is old later */
157            ftruncate(mfd,0) ;
158        }
159
160        /*  Close the actual mail drop */
161        close (mfd);
162    }
163
164    /*  Acquire a stream pointer for the temporary maildrop */
165    if ( (p->drop = fdopen(dfd,"a+")) == NULL ) {
166        close(dfd) ;
167        return pop_msg(p,POP_FAILURE,"Cannot assign stream for %s",
168            p->temp_drop);
169    }
170
171    rewind (p->drop);
172
173    return(POP_SUCCESS);
174}
175