summaryrefslogtreecommitdiffstats
path: root/crypto/heimdal/appl/popper/pop_dropcopy.c
blob: 99ea49d085202882d56280be40242a72a07b4cde (plain)
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
166
167
168
169
170
171
172
173
174
/*
 * Copyright (c) 1989 Regents of the University of California.
 * All rights reserved.  The Berkeley software License Agreement
 * specifies the terms and conditions for redistribution.
 */

#include <popper.h>
RCSID("$Id: pop_dropcopy.c,v 1.26 2002/07/04 14:10:11 joda Exp $");

/*
 * Run as the user in `pwd'
 */

int
changeuser(POP *p, struct passwd *pwd)
{
    if(setgid(pwd->pw_gid) < 0) {
	pop_log (p, POP_PRIORITY,
		 "Unable to change to gid %u: %s",
		 (unsigned)pwd->pw_gid,
		 strerror(errno));
	return pop_msg (p, POP_FAILURE,
			"Unable to change gid");
    }
    if(setuid(pwd->pw_uid) < 0) {
	pop_log (p, POP_PRIORITY,
		 "Unable to change to uid %u: %s",
		 (unsigned)pwd->pw_uid,
		 strerror(errno));
	return pop_msg (p, POP_FAILURE,
			"Unable to change uid");
    }
#ifdef DEBUG
    if(p->debug)
	pop_log(p, POP_DEBUG,"uid = %u, gid = %u",
	       (unsigned)getuid(),
	       (unsigned)getgid());
#endif /* DEBUG */
    return POP_SUCCESS;
}

/* 
 *  dropcopy:   Make a temporary copy of the user's mail drop and 
 *  save a stream pointer for it.
 */

int
pop_dropcopy(POP *p, struct passwd *pwp)
{
    int                     mfd;                    /*  File descriptor for 
                                                        the user's maildrop */
    int                     dfd;                    /*  File descriptor for 
                                                        the SERVER maildrop */
    FILE		    *tf;		    /*  The temp file */
    char		    template[POP_TMPSIZE];  /*  Temp name holder */
    char                    buffer[BUFSIZ];         /*  Read buffer */
    long                    offset;                 /*  Old/New boundary */
    int                     nchar;                  /*  Bytes written/read */
    int                     tf_fd;                  /*  fd for temp file */
    int			    ret;

    /*  Create a temporary maildrop into which to copy the updated maildrop */
    snprintf(p->temp_drop, sizeof(p->temp_drop), POP_DROP,p->user);

#ifdef DEBUG
    if(p->debug)
        pop_log(p,POP_DEBUG,"Creating temporary maildrop '%s'",
            p->temp_drop);
#endif /* DEBUG */

    /* Here we work to make sure the user doesn't cause us to remove or
     * write over existing files by limiting how much work we do while
     * running as root.
     */

    strlcpy(template, POP_TMPDROP, sizeof(template));
    if ((tf_fd = mkstemp(template)) < 0 ||
	(tf = fdopen(tf_fd, "w+")) == NULL) {
        pop_log(p,POP_PRIORITY,
            "Unable to create temporary temporary maildrop '%s': %s",template,
		strerror(errno));
        return pop_msg(p,POP_FAILURE,
		"System error, can't create temporary file.");
    }

    /* Now give this file to the user	*/
    chown(template, pwp->pw_uid, pwp->pw_gid);
    chmod(template, 0600);

    /* Now link this file to the temporary maildrop.  If this fails it
     * is probably because the temporary maildrop already exists.  If so,
     * this is ok.  We can just go on our way, because by the time we try
     * to write into the file we will be running as the user.
     */
    link(template,p->temp_drop);
    fclose(tf);
    unlink(template);

    ret = changeuser(p, pwp);
    if (ret != POP_SUCCESS)
	return ret;

    /* Open for append,  this solves the crash recovery problem */
    if ((dfd = open(p->temp_drop,O_RDWR|O_APPEND|O_CREAT,0600)) == -1){
        pop_log(p,POP_PRIORITY,
            "Unable to open temporary maildrop '%s': %s",p->temp_drop,
		strerror(errno));
        return pop_msg(p,POP_FAILURE,
		"System error, can't open temporary file, do you own it?");
    }

    /*  Lock the temporary maildrop */
    if ( flock (dfd, (LOCK_EX | LOCK_NB)) == -1 ) 
    switch(errno) {
        case EWOULDBLOCK:
            return pop_msg(p,POP_FAILURE,
                 "%sMaildrop lock busy!  Is another session active?", 
			   (p->flags & POP_FLAG_CAPA) ? "[IN-USE] " : "");
            /* NOTREACHED */
        default:
            return pop_msg(p,POP_FAILURE,"flock: '%s': %s", p->temp_drop,
		strerror(errno));
            /* NOTREACHED */
        }
    
    /* May have grown or shrunk between open and lock! */
    offset = lseek(dfd,0, SEEK_END);

    /*  Open the user's maildrop, If this fails,  no harm in assuming empty */
    if ((mfd = open(p->drop_name,O_RDWR)) > 0) {

        /*  Lock the maildrop */
        if (flock (mfd, LOCK_EX) == -1) {
            close(mfd) ;
            return pop_msg(p,POP_FAILURE, "flock: '%s': %s", p->temp_drop,
		strerror(errno));
        }

        /*  Copy the actual mail drop into the temporary mail drop */
        while ( (nchar=read(mfd,buffer,BUFSIZ)) > 0 )
            if ( nchar != write(dfd,buffer,nchar) ) {
                nchar = -1 ;
                break ;
            }

        if ( nchar != 0 ) {
            /* Error adding new mail.  Truncate to original size,
               and leave the maildrop as is.  The user will not 
               see the new mail until the error goes away.
               Should let them process the current backlog,  in case
               the error is a quota problem requiring deletions! */
            ftruncate(dfd,(int)offset) ;
        } else {
            /* Mail transferred!  Zero the mail drop NOW,  that we
               do not have to do gymnastics to figure out what's new
               and what is old later */
            ftruncate(mfd,0) ;
        }

        /*  Close the actual mail drop */
        close (mfd);
    }

    /*  Acquire a stream pointer for the temporary maildrop */
    if ( (p->drop = fdopen(dfd,"a+")) == NULL ) {
        close(dfd) ;
        return pop_msg(p,POP_FAILURE,"Cannot assign stream for %s",
            p->temp_drop);
    }

    rewind (p->drop);

    return(POP_SUCCESS);
}
OpenPOWER on IntegriCloud