1 /*
2  * Copyright 2015-2021 The Regents of the University of California
3  * All rights reserved.
4  *
5  * This file is part of Spoofer.
6  *
7  * Spoofer is free software: you can redistribute it and/or modify
8  * it under the terms of the GNU General Public License as published by
9  * the Free Software Foundation, either version 3 of the License, or
10  * (at your option) any later version.
11  *
12  * Spoofer is distributed in the hope that it will be useful,
13  * but WITHOUT ANY WARRANTY; without even the implied warranty of
14  * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
15  * GNU General Public License for more details.
16  *
17  * You should have received a copy of the GNU General Public License
18  * along with Spoofer.  If not, see <http://www.gnu.org/licenses/>.
19  */
20 
21 #ifndef SPOOFER_MANAGER_COMMON_COMMON_H
22 #define SPOOFER_MANAGER_COMMON_COMMON_H
23 
24 #include "../../config.h"
25 #include <typeinfo>
26 #include <ctime>
27 
28 #include "spoof_qt.h"
29 
30 #include <QDataStream>
31 #include <QCoreApplication>
32 #include <QFile>
33 #include <QTextStream>
34 //#include <QHostAddress>
35 #include <QDateTime>
36 #include <QSettings>
37 #include <QList>
38 
39 #include "port.h"
40 
41 #ifdef UPGRADE_KEY
42  #define AUTOUPGRADE_ENABLED
43  // UPGRADE_CMD is a C++ string literal that will be executed in a shell
44  // script.  $1 is the package file name.
45  #ifndef UPGRADE_CMD
46   #if defined(Q_OS_MACOS)
47    #define UPGRADE_CMD "/usr/sbin/installer -package \"$1\" -target /"
48   #elif defined(Q_OS_WIN32)
49    // UPGRADE_CMD is not used on Windows
50   #endif
51  #endif
52 #endif
53 
54 // forward declarations
55 QT_BEGIN_NAMESPACE
56 class QCommandLineParser;
57 QT_END_NAMESPACE
58 
59 #define EVERYONE_IS_PRIVILEGED 1
60 
61 // scheduler messages
62 enum sc_msg_type {
63     SC_ERROR,
64     SC_TEXT,
65     SC_SCHEDULED,
66     SC_PROBER_STARTED,
67     SC_PROBER_FINISHED,
68     SC_PROBER_ERROR,
69     SC_PAUSED,
70     SC_RESUMED,
71     SC_DONE_CMD,
72     SC_CONFIG_CHANGED,
73     SC_NEED_CONFIG,
74     SC_CONFIGED,
75     SC_HELLO,
76     SC_UPGRADE_AVAILABLE,
77     SC_UPGRADE_PROGRESS,
78     SC_UPGRADE_ERROR,
79     SC_UPGRADE_INSTALLING
80 };
81 
82 struct sc_msg_text {
83     QString text;
textsc_msg_text84     sc_msg_text(const QString &_text = QString()) : text(_text) {}
85 };
86 
87 inline QDataStream &operator<<(QDataStream &out, const sc_msg_text &msg) {
88     return out << msg.text;
89 }
90 
91 inline QDataStream &operator>>(QDataStream &in, sc_msg_text &msg) {
92     return in >> msg.text;
93 }
94 
95 struct sc_msg_scheduled {
96     // QHostAddress addr;
97     qint32 when;
sc_msg_scheduledsc_msg_scheduled98     sc_msg_scheduled() : /*addr(),*/ when(0) {}
99 };
100 
101 inline QDataStream &operator<<(QDataStream &out, const sc_msg_scheduled &msg) {
102     return out /* << msg.addr */ << msg.when;
103 }
104 
105 inline QDataStream &operator>>(QDataStream &in, sc_msg_scheduled &msg) {
106     return in /* >> msg.addr */ >> msg.when;
107 }
108 
109 struct sc_msg_upgrade_available {
110     int autoTime;
111     bool mandatory;
112     int32_t vnum;
113     QString vstr;
114     QString file;
115     QString warning;
116     sc_msg_upgrade_available(bool wantAuto = false, bool _mandatory = false,
117 	int32_t _vnum = 0,
118 	const QString &_vstr = QString(), const QString &_file = QString());
119 };
120 
121 inline QDataStream &operator<<(QDataStream &out, const sc_msg_upgrade_available &msg) {
122     return out << msg.autoTime << msg.mandatory << msg.vnum << msg.vstr << msg.file << msg.warning;
123 }
124 
125 inline QDataStream &operator>>(QDataStream &in, sc_msg_upgrade_available &msg) {
126     return in >> msg.autoTime >> msg.mandatory >> msg.vnum >> msg.vstr >> msg.file >> msg.warning;
127 }
128 
129 #if defined(Q_OS_UNIX)
130 typedef int error_t;
getLastErr()131 inline error_t getLastErr() { return errno; }
132 #elif defined(Q_OS_WIN32)
133 typedef unsigned long error_t;
134 error_t getLastErr();
135 #endif
136 
137 QString getErrmsg(error_t err);
getLastErrmsg()138 inline QString getLastErrmsg() { return getErrmsg(getLastErr()); }
139 bool getProcessName(qint64 pid, char *buf, unsigned len);
140 
141 // base class for Spoofer applications
142 class SpooferBase {
143 public:
144     // A write-only QIODevice that wraps another but doesn't open it until
145     // needed, and can be switched to a different device at any time.
146     class OnDemandDevice Q_DECL_FINAL : public QIODevice {
147     private:
148 	QIODevice *dev, *newdev, *fallback;
149 	QString newname;
150 	bool timestampEnabled;
151 	OnDemandDevice(OnDemandDevice &) NO_METHOD; // no copy-ctor
152 	OnDemandDevice operator=(OnDemandDevice &) NO_METHOD; // no copy-assign
153     public:
OnDemandDevice(FILE * file)154 	OnDemandDevice(FILE *file) :
155 	    QIODevice(), dev(), newdev(), fallback(), newname(), timestampEnabled()
156 	{
157 	    QFile *qfile = new QFile();
158 	    if (qfile) qfile->open(file, WriteOnly|Unbuffered);
159 	    dev = qfile;
160 	    this->open(WriteOnly);
161 	}
readData(char * data,qint64 maxSize)162 	qint64 readData(char *data, qint64 maxSize) OVERRIDE
163 	    { Q_UNUSED(data); Q_UNUSED(maxSize); return -1; }
164 	qint64 writeData(const char *data, qint64 maxSize) OVERRIDE;
setDevice(QIODevice * device,const QString & name)165 	void setDevice(QIODevice *device, const QString &name) {
166 	    newname = name;
167 	    newdev = device;
168 	}
setDevice(QFileDevice * device)169 	void setDevice(QFileDevice *device) {
170 	    setDevice(device, device->fileName());
171 	}
setFallbackDevice(QFileDevice * device)172 	void setFallbackDevice(QFileDevice *device) {
173 	    fallback = device;
174 	}
close()175 	void close() OVERRIDE {
176 	    QIODevice::close();
177 	    if (dev) { delete dev; dev = nullptr; }
178 	    if (newdev) { delete newdev; newdev = nullptr; }
179 	    if (fallback) { delete fallback; fallback = nullptr; }
180 	}
type()181 	const std::type_info& type() { return dev ? typeid(*dev) : typeid(0); }
setTimestampEnabled(bool flag)182 	void setTimestampEnabled(bool flag) { timestampEnabled = flag; }
getTimestampEnabled()183 	bool getTimestampEnabled() const { return timestampEnabled; }
184     };
185 
186     class Config {
187 	bool forWriting;
188 	ATR_UNUSED_MEMBER uint8_t unused_padding[3];
189     public:
190 	struct MemberBase {
191 	    const QString key;
192 	    QVariant defaultVal;
193 	    bool required;
194 	    const QString desc;
195 	    MemberBase(QString _key, QVariant _defaultVal, QString _desc = QString(), bool _hidden = false) :
keyMemberBase196 		key(_key), defaultVal(_defaultVal), required(false), desc(_desc)
197 	    {
198 		if (_hidden || desc.isNull()) return;
199 #ifdef EVERYONE_IS_PRIVILEGED
200 		if (_key.startsWith(QSL("unpriv"))) return;
201 #endif
202 		members.push_back(this);
203 	    }
~MemberBaseMemberBase204 	    virtual ~MemberBase() {}
variantMemberBase205 	    QVariant variant() const
206 		{ return settings ? settings->value(key, defaultVal) : defaultVal; }
207 	    virtual bool setFromString(QString value, QString &errmsg) = 0;
removeMemberBase208 	    void remove()            // remove: config->foo.remove()
209 		{ if (settings) settings->remove(key); }
setDefaultMemberBase210 	    void setDefault(QVariant d) { defaultVal = d; }
isSetMemberBase211 	    bool isSet() const
212 		{ return settings && settings->contains(key); }
213 	    virtual QString optionHelpString() = 0;
enforceMemberBase214 	    virtual void enforce() {}
validateMemberBase215 	    virtual bool validate(QVariant var, QString &errmsg)
216 		{ Q_UNUSED(var); Q_UNUSED(errmsg); return true; }
217 	};
218 	template <class T> struct Member : public MemberBase {
219 	    Member(QString _key, T _defaultVal = T(), QString _desc = QString(), bool _hidden = false) :
MemberBaseMember220 		MemberBase(_key, QVariant(_defaultVal), _desc, _hidden) {}
~MemberMember221 	    ~Member() {}
operatorMember222 	    T operator()() const // get: config->foo()
223 		{ return variant().template value<T>(); }
operatorMember224 	    void operator()(T value) // set: config->foo(value)
225 		{ if (settings) settings->setValue(key, QVariant(value)); }
setFromStringMember226 	    bool setFromString(QString value, QString &errmsg) OVERRIDE {
227 		if (!settings) return false;
228 		QVariant var(value);
229 		int qtypeid = qMetaTypeId<T>();
230 		if (!var.convert(qtypeid)) {
231 		    errmsg = QSL("%1: can not convert \"%2\" to %3.").
232 			arg(key).arg(value).
233 			arg(QString::fromLocal8Bit(QMetaType::typeName(qtypeid)));
234 		    qDebug() << errmsg;
235 		    return false;
236 		}
237 		if (!validate(var, errmsg)) return false;
238 		settings->setValue(key, var);
239 		return true;
240 	    }
optionHelpStringMember241 	    QString optionHelpString() OVERRIDE
242 		{ return optionHelpString(*this); }
243 	private:
optionHelpStringMember244 	    template <typename U> QString optionHelpString(const Member<U> &member) {
245 		Q_UNUSED(member);
246 		return QSL("%1 from now on [\"%2\" setting or \"%3\"].").
247 		    arg(desc).arg(key).arg(defaultVal.toString());
248 	    }
249 	    // C++ doesn't allow an explicit template specialization inside a
250 	    // class, but does allow an overload.  (The unused parameter is
251 	    // just for overload resolution.)
optionHelpStringMember252 	    QString optionHelpString(const Member<bool> &member) {
253 		Q_UNUSED(member);
254 		return QSL("%1 from now on (yes/no) [\"%2\" setting or %3].").
255 		    arg(desc).arg(key).arg(defaultVal.toInt());
256 	    }
257 	};
258 	struct MemberInt Q_DECL_FINAL : public Member<int> {
259 	    int minVal, maxVal;
260 	    MemberInt(QString _key, int _defaultVal, int _minVal, int _maxVal,
261 		QString _desc = QString(), bool _hidden = false) :
MemberQ_DECL_FINAL262 		Member(_key, _defaultVal, _desc, _hidden),
263 		minVal(_minVal), maxVal(_maxVal)
264 		{}
265 	private:
validateQ_DECL_FINAL266 	    bool validate(QVariant var, QString &errmsg) OVERRIDE {
267 		int val = var.value<int>();
268 		if (val >= minVal && val <= maxVal) return true;
269 		errmsg = QSL("%1: value %2 out of range [%3, %4]").
270 		    arg(key).arg(val).arg(minVal).arg(maxVal);
271 		qDebug() << errmsg;
272 		return false;
273 	    }
enforceQ_DECL_FINAL274 	    void enforce() OVERRIDE {
275 		if (!settings) return;
276 		int val = variant().value<int>();
277 		if (val < minVal)
278 		    settings->setValue(key, QVariant(minVal));
279 		if (val > maxVal)
280 		    settings->setValue(key, QVariant(maxVal));
281 	    }
282 	};
283 	static QSettings *settings;
284     public:
285 	static QList<MemberBase*> members;
286 
287 	Member<QString> dataDir;
288 	Member<QString> schedulerSocketName;
289 	Member<bool> paused;
290 #if DEBUG
291 	Member<bool> useDevServer;
292 	MemberInt spooferProtocolVersion;
293 	Member<bool> pretendMode;
294 	Member<bool> standaloneMode;
295 	Member<bool> installerAddTaint;
296 	Member<bool> installerVerifySig;
297 	Member<bool> installerKeep;
298 #else // DEBUG
useDevServer()299 	bool useDevServer() { return false; }
pretendMode()300 	bool pretendMode() { return false; }
standaloneMode()301 	bool standaloneMode() { return false; }
installerAddTaint()302 	bool installerAddTaint() { return true; }
installerVerifySig()303 	bool installerVerifySig() { return true; }
installerKeep()304 	bool installerKeep() { return false; }
305 #endif // DEBUG
306 
307 #ifdef AUTOUPGRADE_ENABLED
308 	Member<bool> autoUpgrade;
309 #endif
310 	Member<bool> enableIPv4;
311 	Member<bool> enableIPv6;
312 	MemberInt keepLogs;
313 	Member<bool> sharePublic;
314 	Member<bool> shareRemedy;
315 	Member<bool> enableTLS;
316 	MemberInt netPollInterval;
317 	MemberInt delayInterval;
318 	MemberInt proberInterval;
319 	MemberInt proberRetryInterval;
320 	MemberInt maxRetries;
321 	Member<bool> unprivView;
322 	Member<bool> unprivTest;
323 	Member<bool> unprivPref;
324 
325 	Config();
326 	void initSettings(bool forWriting = false, bool debug = false);
~Config()327 	~Config() { if (settings) delete settings; settings = nullptr; }
328 
329 	bool error(const char *label);
330 	void logError(const char *label, QString msg, QString msg2 = QString());
331 	QString lockFileName();
332 	void remove();
333 
fileName()334 	QString fileName() {
335 	    return settings ? settings->fileName() : QString();
336 	}
337 
isFile()338 	bool isFile() {
339 	    if (!settings) return false;
340 #ifdef Q_OS_WIN32
341 	    if (settings->format() == QSettings::NativeFormat)
342 		return false; // windows registry
343 #endif
344 	    return true;
345 	}
346 
sync()347 	bool sync() {
348 	    if (!settings) return false;
349 	    settings->sync();
350 	    return !error("Config sync:");
351 	}
352 
find(const QString & key)353 	MemberBase *find(const QString &key) {
354 	    for (int i = 0; i < members.size(); i++) {
355 		if (key.compare(members[i]->key, Qt::CaseInsensitive) == 0)
356 		    return members[i];
357 	    }
358 	    return nullptr;
359 	}
360 
hasRequiredSettings()361 	bool hasRequiredSettings() {
362 	    for (auto m : members) {
363 		if (!m->isSet() && m->required)
364 		    return false;
365 	    }
366 	    return true;
367 	}
368 
enforce()369 	void enforce() {
370 	    for (auto m : members) {
371 		m->enforce();
372 	    }
373 	}
374     };
375 
376     QString appDir;
377     QString appFile;
378     static const QStringList *args; // alternate command line arguments
379     static QString optSettings;
380     static Config *config;
381     static OnDemandDevice outdev;
382     static OnDemandDevice errdev;
383     static QTextStream spout;
384     static QTextStream sperr;
385     static const QString proberLogFtime;
386     static const QString proberLogGlob;
387     static const QString proberLogRegex;
388 
389     SpooferBase();
390 
~SpooferBase()391     virtual ~SpooferBase() {
392 	if (config) delete config;
393     }
394 
395     static void logHandler(QtMsgType type,
396 	const QMessageLogContext &ctx, const QString &msg);
397     static bool parseCommandLine(QCommandLineParser &clp, QString desc);
398     static QString ftime_zone(const QString &fmt, const time_t *tp, const Qt::TimeSpec &spec);
399     static QString ftime(const QString &fmt = QString(), const time_t *tp = nullptr)
400 	{ return ftime_zone(fmt, tp, Qt::LocalTime); }
401     static QString ftime_utc(const QString &fmt = QString(), const time_t *tp = nullptr)
402 	{ return ftime_zone(fmt, tp, Qt::UTC); }
403 
findDefaultSettings(bool debug)404     static QSettings *findDefaultSettings(bool debug)
405     {
406 	QSettings::Scope scope = debug ? QSettings::UserScope :
407 	    QSettings::SystemScope;
408 	// Mac expects a domain here where others expect a name.
409 	// QSettings() with no parameters would correctly auto-pick
410 	// the domain or name, but doesn't let us specify the scope.
411 	return new QSettings(scope,
412 #ifdef Q_OS_MACOS
413 	    QCoreApplication::organizationDomain(),
414 #else
415 	    QCoreApplication::organizationName(),
416 #endif
417 	    QCoreApplication::applicationName(), nullptr);
418     }
419 
420     bool initConfig(bool _forWriting = false) {
421 	config->initSettings(_forWriting);
422 	if (config->error("Config")) // XXX ???
423 	    return false;
424 	if (!_forWriting && config->dataDir().isEmpty()) {
425 	    // QSettings doesn't consider a missing file an error.  But if
426 	    // we want read-only and dataDir is missing, something is wrong.
427 	    config->logError("Config", QSL("Missing \"dataDir\""),
428 		QSL("Make sure the scheduler is running and using the same configuration."));
429 	    return false;
430 	}
431 	return true;
432     }
433 };
434 
435 #endif // SPOOFER_MANAGER_COMMON_COMMON_H
436