1 /*
2 * Copyright (c) 2010 Tomas Mraz <tmraz@redhat.com>
3 * Copyright (c) 2010, 2016, 2017 Red Hat, Inc.
4 *
5 * Redistribution and use in source and binary forms, with or without
6 * modification, are permitted provided that the following conditions
7 * are met:
8 * 1. Redistributions of source code must retain the above copyright
9 * notice, and the entire permission notice in its entirety,
10 * including the disclaimer of warranties.
11 * 2. Redistributions in binary form must reproduce the above copyright
12 * notice, this list of conditions and the following disclaimer in the
13 * documentation and/or other materials provided with the distribution.
14 * 3. The name of the author may not be used to endorse or promote
15 * products derived from this software without specific prior
16 * written permission.
17 *
18 * ALTERNATIVELY, this product may be distributed under the terms of
19 * the GNU Public License, in which case the provisions of the GPL are
20 * required INSTEAD OF the above restrictions. (This clause is
21 * necessary due to a potential bad interaction between the GPL and
22 * the restrictions contained in a BSD-style copyright.)
23 *
24 * THIS SOFTWARE IS PROVIDED ``AS IS'' AND ANY EXPRESS OR IMPLIED
25 * WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES
26 * OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE
27 * DISCLAIMED. IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR ANY DIRECT,
28 * INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES
29 * (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR
30 * SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION)
31 * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT,
32 * STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE)
33 * ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED
34 * OF THE POSSIBILITY OF SUCH DAMAGE.
35 */
36
37 #include "config.h"
38 #include <string.h>
39 #include <stdlib.h>
40 #include <unistd.h>
41 #include <errno.h>
42 #include <sys/types.h>
43 #include <sys/stat.h>
44 #include <sys/file.h>
45 #include <sys/stat.h>
46 #include <fcntl.h>
47 #include <security/pam_modutil.h>
48
49 #include "faillock.h"
50
51 #define ignore_return(x) if (1==((int)x)) {;}
52
53 int
54 open_tally (const char *dir, const char *user, uid_t uid, int create)
55 {
56 char *path;
57 int flags = O_RDWR;
58 int fd;
59
60 if (dir == NULL || strstr(user, "../") != NULL)
61 /* just a defensive programming as the user must be a
62 * valid user on the system anyway
63 */
64 return -1;
65 path = malloc(strlen(dir) + strlen(user) + 2);
66 if (path == NULL)
67 return -1;
68
69 strcpy(path, dir);
70 if (*dir && dir[strlen(dir) - 1] != '/') {
71 strcat(path, "/");
72 }
73 strcat(path, user);
74
75 if (create) {
76 flags |= O_CREAT;
77 if (access(dir, F_OK) != 0) {
78 mkdir(dir, 0755);
79 }
80 }
81
82 fd = open(path, flags, 0660);
83
84 free(path);
85
86 if (fd != -1) {
87 struct stat st;
88
89 while (flock(fd, LOCK_EX) == -1 && errno == EINTR);
90 if (fstat(fd, &st) == 0) {
91 if (st.st_uid != uid) {
92 ignore_return(fchown(fd, uid, -1));
93 }
94
95 /*
96 * If umask is set to 022, as will probably in most systems, then the
97 * group will not be able to write to the file. So, change the file
98 * permissions just in case.
99 * Note: owners of this file are user:root, so if the permissions are
100 * not changed the root process writing to this file will require
101 * CAP_DAC_OVERRIDE.
102 */
103 if (!(st.st_mode & S_IWGRP)) {
104 ignore_return(fchmod(fd, 0660));
105 }
106 }
107 }
108
109 return fd;
110 }
111
112 #define CHUNK_SIZE (64 * sizeof(struct tally))
113 #define MAX_RECORDS 1024
114
115 int
116 read_tally(int fd, struct tally_data *tallies)
117 {
118 void *data = NULL, *newdata;
119 unsigned int count = 0;
120 ssize_t chunk = 0;
121
122 do {
123 newdata = realloc(data, count * sizeof(struct tally) + CHUNK_SIZE);
124 if (newdata == NULL) {
125 free(data);
126 return -1;
127 }
128
129 data = newdata;
130
131 chunk = pam_modutil_read(fd, (char *)data + count * sizeof(struct tally), CHUNK_SIZE);
132 if (chunk < 0) {
133 free(data);
134 return -1;
135 }
136
137 count += chunk/sizeof(struct tally);
138
139 if (count >= MAX_RECORDS)
140 break;
141 }
142 while (chunk == CHUNK_SIZE);
143
144 tallies->records = data;
145 tallies->count = count;
146
147 return 0;
148 }
149
150 int
151 update_tally(int fd, struct tally_data *tallies)
152 {
153 void *data = tallies->records;
154 unsigned int count = tallies->count;
155 ssize_t chunk;
156
157 if (tallies->count > MAX_RECORDS) {
158 data = tallies->records + (count - MAX_RECORDS);
159 count = MAX_RECORDS;
160 }
161
162 if (lseek(fd, 0, SEEK_SET) == (off_t)-1) {
163 return -1;
164 }
165
166 chunk = pam_modutil_write(fd, data, count * sizeof(struct tally));
167
168 if (chunk != (ssize_t)(count * sizeof(struct tally))) {
169 return -1;
170 }
171
172 if (ftruncate(fd, count * sizeof(struct tally)) == -1)
173 return -1;
174
175 return 0;
176 }