1 /*
2     --------------------------------------------------------------------
3     CT Host Implementation
4     --------------------------------------------------------------------
5     SPDX-FileCopyrightText: 1999 Gary Meyer <gary@meyer.net>
6     --------------------------------------------------------------------
7     SPDX-License-Identifier: GPL-2.0-or-later
8 */
9 
10 #include "cthost.h"
11 
12 #include <pwd.h>
13 #include <sys/types.h>
14 #include <unistd.h> // getuid()
15 
16 #include <QFile>
17 #include <QTextStream>
18 
19 #include <KLocalizedString>
20 
21 #include "crontabWidget.h"
22 
23 #include "ctInitializationError.h"
24 #include "ctSystemCron.h"
25 #include "ctcron.h"
26 
27 #include "kcm_cron_debug.h"
28 
CTHost(const QString & cronBinary,CTInitializationError & ctInitializationError)29 CTHost::CTHost(const QString &cronBinary, CTInitializationError &ctInitializationError)
30 {
31     struct passwd *userInfos = nullptr;
32 
33     mCrontabBinary = cronBinary;
34 
35     // If it is the root user
36     if (getuid() == 0) {
37         // Read /etc/passwd
38         setpwent(); // restart
39         while ((userInfos = getpwent())) {
40             if (allowDeny(userInfos->pw_name)) {
41                 const QString errorMessage = createCTCron(userInfos);
42                 if (!errorMessage.isEmpty()) {
43                     ctInitializationError.setErrorMessage(errorMessage);
44                     return;
45                 }
46             }
47             // delete userInfos;
48         }
49         setpwent(); // restart again for others
50     }
51     // Non-root user, so just create user's cron table.
52     else {
53         // Get name from UID, check it against AllowDeny()
54         unsigned int uid = getuid();
55         setpwent(); // restart
56         while ((userInfos = getpwent())) {
57             if ((userInfos->pw_uid == uid) && (!allowDeny(userInfos->pw_name))) {
58                 ctInitializationError.setErrorMessage(
59                     i18n("You have been blocked from using KCron\
60 	                      by either the /etc/cron.allow file or the /etc/cron.deny file.\
61 	                      \n\nCheck the crontab man page for further details."));
62 
63                 return;
64             }
65             // delete userInfos;
66         }
67 
68         setpwent(); // restart again for others
69 
70         passwd *currentUserPassword = getpwuid(uid);
71 
72         const QString errorMessage = createCTCron(currentUserPassword);
73         if (!errorMessage.isEmpty()) {
74             ctInitializationError.setErrorMessage(errorMessage);
75             return;
76         }
77 
78         // delete currentUserPassword;
79     }
80     // Create the system cron table.
81     createSystemCron();
82 }
83 
~CTHost()84 CTHost::~CTHost()
85 {
86     qDeleteAll(mCrons);
87 }
88 
allowDeny(char * name)89 bool CTHost::allowDeny(char *name)
90 {
91     QFile allow(QStringLiteral("/etc/cron.allow"));
92 
93     // if cron.allow exists make sure user is listed
94     if (allow.open(QFile::ReadOnly)) {
95         QTextStream stream(&allow);
96         while (!stream.atEnd()) {
97             if (stream.readLine() == QLatin1String(name)) {
98                 allow.close();
99                 return true;
100             }
101         }
102         allow.close();
103         return false;
104     } else {
105         allow.close();
106         QFile deny(QStringLiteral("/etc/cron.deny"));
107 
108         // else if cron.deny exists make sure user is not listed
109         if (deny.open(QFile::ReadOnly)) {
110             QTextStream stream(&deny);
111             while (!stream.atEnd()) {
112                 if (stream.readLine() == QLatin1String(name)) {
113                     deny.close();
114                     return false;
115                 }
116             }
117             deny.close();
118             return true;
119         } else {
120             deny.close();
121             return true;
122         }
123     }
124 }
125 
save(CrontabWidget * mCrontabWidget)126 CTSaveStatus CTHost::save(CrontabWidget *mCrontabWidget)
127 {
128     qCDebug(KCM_CRON_LOG) << "Save current cron.";
129     // Retrieve the current cron to use. This could either be a user cron or a system cron.
130     // Implements system cron entry point.
131     CTCron *ctCron = mCrontabWidget->currentCron();
132 
133     return ctCron->save();
134 }
135 
cancel()136 void CTHost::cancel()
137 {
138     for (CTCron *ctCron : std::as_const(mCrons)) {
139         ctCron->cancel();
140     }
141 }
142 
isDirty()143 bool CTHost::isDirty()
144 {
145     bool isDirty = false;
146 
147     for (CTCron *ctCron : std::as_const(mCrons)) {
148         if (ctCron->isDirty()) {
149             isDirty = true;
150         }
151     }
152 
153     return isDirty;
154 }
155 
createSystemCron()156 CTCron *CTHost::createSystemCron()
157 {
158     CTCron *p = new CTSystemCron(mCrontabBinary);
159 
160     mCrons.append(p);
161 
162     return p;
163 }
164 
createCTCron(const struct passwd * userInfos)165 QString CTHost::createCTCron(const struct passwd *userInfos)
166 {
167     bool currentUserCron = false;
168     if (userInfos->pw_uid == getuid()) {
169         currentUserCron = true;
170     }
171 
172     CTInitializationError ctInitializationError;
173     auto p = new CTCron(mCrontabBinary, userInfos, currentUserCron, ctInitializationError);
174     if (ctInitializationError.hasErrorMessage()) {
175         delete p;
176         return ctInitializationError.errorMessage();
177     }
178 
179     mCrons.append(p);
180 
181     return QString();
182 }
183 
findCurrentUserCron() const184 CTCron *CTHost::findCurrentUserCron() const
185 {
186     // Because multiple users may exist, return only the currently logged in user's cron in user cron mode.
187     for (CTCron *ctCron : std::as_const(mCrons)) {
188         if (ctCron->isCurrentUserCron()) {
189             return ctCron;
190         }
191     }
192 
193     qCDebug(KCM_CRON_LOG) << "Unable to find the current user Cron. Please report this bug and your crontab config to the developers.";
194     return nullptr;
195 }
196 
findSystemCron() const197 CTCron *CTHost::findSystemCron() const
198 {
199     // Return the cron belonging to root.
200     for (CTCron *ctCron : std::as_const(mCrons)) {
201         if (ctCron->isMultiUserCron()) {
202             return ctCron;
203         }
204     }
205 
206     qCDebug(KCM_CRON_LOG) << "Unable to find the system Cron. Please report this bug and your crontab config to the developers.";
207     return nullptr;
208 }
209 
findUserCron(const QString & userLogin) const210 CTCron *CTHost::findUserCron(const QString &userLogin) const
211 {
212     for (CTCron *ctCron : std::as_const(mCrons)) {
213         if (ctCron->userLogin() == userLogin) {
214             return ctCron;
215         }
216     }
217 
218     qCDebug(KCM_CRON_LOG) << "Unable to find the user Cron " << userLogin << ". Please report this bug and your crontab config to the developers.";
219     return nullptr;
220 }
221 
findCronContaining(CTTask * ctTask) const222 CTCron *CTHost::findCronContaining(CTTask *ctTask) const
223 {
224     for (CTCron *ctCron : std::as_const(mCrons)) {
225         if (ctCron->tasks().contains(ctTask)) {
226             return ctCron;
227         }
228     }
229 
230     qCDebug(KCM_CRON_LOG) << "Unable to find the cron of this task. Please report this bug and your crontab config to the developers.";
231     return nullptr;
232 }
233 
findCronContaining(CTVariable * ctVariable) const234 CTCron *CTHost::findCronContaining(CTVariable *ctVariable) const
235 {
236     for (CTCron *ctCron : std::as_const(mCrons)) {
237         if (ctCron->variables().contains(ctVariable)) {
238             return ctCron;
239         }
240     }
241 
242     qCDebug(KCM_CRON_LOG) << "Unable to find the cron of this variable. Please report this bug and your crontab config to the developers.";
243     return nullptr;
244 }
245