00001
00002
00003
00004
00005
00006
00007
00008
00009
00010
00011 #include "wvdbusserver.h"
00012 #include "wvdbusconn.h"
00013 #include "wvstrutils.h"
00014 #include "wvuid.h"
00015 #include "wvtcplistener.h"
00016 #include "wvdelayedcallback.h"
00017 #undef interface // windows
00018 #include <dbus/dbus.h>
00019 #include "wvx509.h"
00020
00021
00022 class WvDBusServerAuth : public IWvDBusAuth
00023 {
00024 enum State { NullWait, AuthWait, BeginWait };
00025 State state;
00026 wvuid_t client_uid;
00027 public:
00028 WvDBusServerAuth();
00029 virtual bool authorize(WvDBusConn &c);
00030
00031 virtual wvuid_t get_uid() { return client_uid; }
00032 };
00033
00034
00035 WvDBusServerAuth::WvDBusServerAuth()
00036 {
00037 state = NullWait;
00038 client_uid = WVUID_INVALID;
00039 }
00040
00041
00042 bool WvDBusServerAuth::authorize(WvDBusConn &c)
00043 {
00044 c.log("State=%s\n", state);
00045 if (state == NullWait)
00046 {
00047 char buf[1];
00048 size_t len = c.read(buf, 1);
00049 if (len == 1 && buf[0] == '\0')
00050 {
00051 state = AuthWait;
00052
00053 }
00054 else if (len > 0)
00055 c.seterr("Client didn't start with NUL byte");
00056 else
00057 return false;
00058 }
00059
00060 const char *line = c.in();
00061 if (!line)
00062 return false;
00063
00064 WvStringList words;
00065 words.split(line);
00066 WvString cmd(words.popstr());
00067
00068 if (state == AuthWait)
00069 {
00070 if (!strcasecmp(cmd, "AUTH"))
00071 {
00072
00073 WvString typ(words.popstr());
00074 if (!strcasecmp(typ, "EXTERNAL"))
00075 {
00076 WvString uid =
00077 WvHexDecoder().strflushstr(words.popstr());
00078 if (!!uid)
00079 {
00080
00081 client_uid = uid.num();
00082 }
00083
00084 state = BeginWait;
00085 c.out("OK f00f\r\n");
00086 }
00087 else
00088 {
00089
00090
00091
00092 c.out("REJECTED EXTERNAL\r\n");
00093
00094 }
00095 }
00096 else
00097 c.seterr("AUTH command expected: '%s'", line);
00098 }
00099 else if (state == BeginWait)
00100 {
00101 if (!strcasecmp(cmd, "BEGIN"))
00102 return true;
00103 else
00104 c.seterr("BEGIN command expected: '%s'", line);
00105 }
00106
00107 return false;
00108 }
00109
00110
00111 WvDBusServer::WvDBusServer()
00112 : log("DBus Server", WvLog::Debug)
00113 {
00114
00115 add(&listeners, false, "listeners");
00116 }
00117
00118
00119 WvDBusServer::~WvDBusServer()
00120 {
00121 close();
00122 zap();
00123 }
00124
00125
00126 void WvDBusServer::listen(WvStringParm moniker)
00127 {
00128 IWvListener *listener = IWvListener::create(moniker);
00129 log(WvLog::Info, "Listening on '%s'\n", *listener->src());
00130 if (!listener->isok())
00131 log(WvLog::Info, "Can't listen: %s\n",
00132 listener->errstr());
00133 listener->onaccept(wv::bind(&WvDBusServer::new_connection_cb,
00134 this, _1));
00135 listeners.add(listener, true, "listener");
00136 }
00137
00138
00139 bool WvDBusServer::isok() const
00140 {
00141 if (geterr())
00142 return false;
00143
00144 WvIStreamList::Iter i(listeners);
00145 for (i.rewind(); i.next(); )
00146 if (!i->isok())
00147 return false;
00148 return WvIStreamList::isok();
00149 }
00150
00151
00152 int WvDBusServer::geterr() const
00153 {
00154 return WvIStreamList::geterr();
00155 }
00156
00157
00158 WvString WvDBusServer::get_addr()
00159 {
00160
00161 WvIStreamList::Iter i(listeners);
00162 for (i.rewind(); i.next(); )
00163 if (i->isok())
00164 return WvString("tcp:%s", *i->src());
00165 return WvString();
00166 }
00167
00168
00169 void WvDBusServer::register_name(WvStringParm name, WvDBusConn *conn)
00170 {
00171 name_to_conn[name] = conn;
00172 }
00173
00174
00175 void WvDBusServer::unregister_name(WvStringParm name, WvDBusConn *conn)
00176 {
00177 assert(name_to_conn[name] == conn);
00178 name_to_conn.erase(name);
00179 }
00180
00181
00182 void WvDBusServer::unregister_conn(WvDBusConn *conn)
00183 {
00184 {
00185 std::map<WvString,WvDBusConn*>::iterator i;
00186 for (i = name_to_conn.begin(); i != name_to_conn.end(); )
00187 {
00188 if (i->second == conn)
00189 {
00190 name_to_conn.erase(i->first);
00191 i = name_to_conn.begin();
00192 }
00193 else
00194 ++i;
00195 }
00196 }
00197
00198 all_conns.unlink(conn);
00199 }
00200
00201
00202 bool WvDBusServer::do_server_msg(WvDBusConn &conn, WvDBusMsg &msg)
00203 {
00204 WvString method(msg.get_member());
00205
00206 if (msg.get_path() == "/org/freedesktop/DBus/Local")
00207 {
00208 if (method == "Disconnected")
00209 return true;
00210 }
00211
00212 if (msg.get_dest() != "org.freedesktop.DBus") return false;
00213
00214
00215
00216
00217
00218
00219 if (method == "Hello")
00220 {
00221 log("hello_cb\n");
00222 msg.reply().append(conn.uniquename()).send(conn);
00223 return true;
00224 }
00225 else if (method == "RequestName")
00226 {
00227 WvDBusMsg::Iter args(msg);
00228 WvString _name = args.getnext();
00229
00230
00231 log("request_name_cb(%s)\n", _name);
00232 register_name(_name, &conn);
00233
00234 msg.reply().append((uint32_t)DBUS_REQUEST_NAME_REPLY_PRIMARY_OWNER)
00235 .send(conn);
00236 return true;
00237 }
00238 else if (method == "ReleaseName")
00239 {
00240 WvDBusMsg::Iter args(msg);
00241 WvString _name = args.getnext();
00242
00243 log("release_name_cb(%s)\n", _name);
00244 unregister_name(_name, &conn);
00245
00246 msg.reply().append((uint32_t)DBUS_RELEASE_NAME_REPLY_RELEASED)
00247 .send(conn);
00248 return true;
00249 }
00250 else if (method == "NameHasOwner")
00251 {
00252 WvDBusMsg::Iter args(msg);
00253 WvString known_name = args.getnext();
00254 WvDBusConn *serv = name_to_conn[known_name];
00255 msg.reply().append(!!serv).send(conn);
00256 return true;
00257 }
00258 else if (method == "GetNameOwner")
00259 {
00260 WvDBusMsg::Iter args(msg);
00261 WvString known_name = args.getnext();
00262 WvDBusConn *serv = name_to_conn[known_name];
00263 if (serv)
00264 msg.reply().append(serv->uniquename()).send(conn);
00265 else
00266 WvDBusError(msg, "org.freedesktop.DBus.Error.NameHasNoOwner",
00267 "No match for name '%s'", known_name).send(conn);
00268 return true;
00269 }
00270 else if (method == "AddMatch")
00271 {
00272
00273 msg.reply().send(conn);
00274 return true;
00275 }
00276 else if (method == "StartServiceByName")
00277 {
00278
00279
00280 msg.reply().send(conn);
00281 return true;
00282 }
00283 else if (method == "GetConnectionUnixUser" ||
00284 method == "GetConnectionUnixUserName")
00285 {
00286 WvDBusMsg::Iter args(msg);
00287 WvString _name = args.getnext();
00288 WvDBusConn *target = name_to_conn[_name];
00289
00290 if (!target)
00291 {
00292 WvDBusError(msg, "org.freedesktop.DBus.Error.Failed",
00293 "No connection found for name '%s'.", _name).send(conn);
00294 return true;
00295 }
00296
00297 wvuid_t client_uid = target->get_uid();
00298
00299 if (client_uid == WVUID_INVALID)
00300 {
00301 WvDBusError(msg, "org.freedesktop.DBus.Error.Failed",
00302 "No user associated with connection '%s'.",
00303 target->uniquename()).send(conn);
00304 return true;
00305 }
00306
00307 log("Found unix user for '%s', uid is %s.\n", _name, client_uid);
00308
00309 if (method == "GetConnectionUnixUser")
00310 {
00311 WvString s(client_uid);
00312 msg.reply().append((uint32_t)atoll(s)).send(conn);
00313 return true;
00314 }
00315 else if (method == "GetConnectionUnixUserName")
00316 {
00317 WvString username = wv_username_from_uid(client_uid);
00318 if (!username)
00319 {
00320 WvDBusError(msg, "org.freedesktop.DBus.Error.Failed",
00321 "No username for uid='%s'", client_uid)
00322 .send(conn);
00323 return true;
00324 }
00325
00326 msg.reply().append(username).send(conn);
00327 return true;
00328 }
00329 else
00330 assert(false);
00331
00332 assert(false);
00333 }
00334 else if (method == "GetConnectionCert" ||
00335 method == "GetConnectionCertFingerprint")
00336 {
00337 WvDBusMsg::Iter args(msg);
00338 WvString connid = args.getnext();
00339
00340 WvDBusConn *c = name_to_conn[connid];
00341
00342 WvString ret = c ? c->getattr("peercert") : WvString::null;
00343 if (ret.isnull())
00344 WvDBusError(msg, "org.freedesktop.DBus.Error.Failed",
00345 "Connection %s did not present a certificate",
00346 connid).send(conn);
00347 else
00348 {
00349 if (method == "GetConnectionCertFingerprint")
00350 {
00351 WvX509 tempcert;
00352
00353 tempcert.decode(WvX509::CertPEM, ret);
00354 ret = tempcert.get_fingerprint();
00355 }
00356 msg.reply().append(ret).send(conn);
00357 }
00358
00359 return true;
00360 }
00361 else
00362 {
00363 WvDBusError(msg, "org.freedesktop.DBus.Error.UnknownMethod",
00364 "Unknown dbus method '%s'", method).send(conn);
00365 return true;
00366 }
00367 }
00368
00369
00370 bool WvDBusServer::do_bridge_msg(WvDBusConn &conn, WvDBusMsg &msg)
00371 {
00372
00373
00374 if (!!msg.get_dest())
00375 {
00376 std::map<WvString,WvDBusConn*>::iterator i
00377 = name_to_conn.find(msg.get_dest());
00378 WvDBusConn *dconn = (i == name_to_conn.end()) ? NULL : i->second;
00379 log("Proxying #%s -> %s\n",
00380 msg.get_serial(),
00381 dconn ? dconn->uniquename() : WvString("(UNKNOWN)"));
00382 dbus_message_set_sender(msg, conn.uniquename().cstr());
00383 if (dconn)
00384 dconn->send(msg);
00385 else
00386 {
00387 log(WvLog::Warning,
00388 "Proxy: no connection for '%s'\n", msg.get_dest());
00389 return false;
00390 }
00391 return true;
00392 }
00393
00394 return false;
00395 }
00396
00397
00398 bool WvDBusServer::do_broadcast_msg(WvDBusConn &conn, WvDBusMsg &msg)
00399 {
00400 if (!msg.get_dest())
00401 {
00402 log("Broadcasting #%s\n", msg.get_serial());
00403
00404
00405
00406
00407
00408 WvDBusConnList::Iter i(all_conns);
00409 for (i.rewind(); i.next(); )
00410 i->send(msg);
00411 return true;
00412 }
00413 return false;
00414 }
00415
00416
00417 bool WvDBusServer::do_gaveup_msg(WvDBusConn &conn, WvDBusMsg &msg)
00418 {
00419 WvDBusError(msg, "org.freedesktop.DBus.Error.NameHasNoOwner",
00420 "No running service named '%s'", msg.get_dest()).send(conn);
00421 return true;
00422 }
00423
00424
00425 void WvDBusServer::conn_closed(WvStream &s)
00426 {
00427 WvDBusConn *c = (WvDBusConn *)&s;
00428 unregister_conn(c);
00429 this->release();
00430 }
00431
00432
00433 void WvDBusServer::new_connection_cb(IWvStream *s)
00434 {
00435 WvDBusConn *c = new WvDBusConn(s, new WvDBusServerAuth, false);
00436 c->addRef();
00437 this->addRef();
00438 all_conns.append(c, true);
00439 register_name(c->uniquename(), c);
00440
00441
00442
00443
00444
00445
00446
00447
00448
00449 IWvStreamCallback mycb = wv::bind(&WvDBusServer::conn_closed, this,
00450 wv::ref(*c));
00451 c->setclosecallback(wv::delayed(mycb));
00452
00453 c->add_callback(WvDBusConn::PriSystem,
00454 wv::bind(&WvDBusServer::do_server_msg, this,
00455 wv::ref(*c), _1));
00456 c->add_callback(WvDBusConn::PriBridge,
00457 wv::bind(&WvDBusServer::do_bridge_msg, this,
00458 wv::ref(*c), _1));
00459 c->add_callback(WvDBusConn::PriBroadcast,
00460 wv::bind(&WvDBusServer::do_broadcast_msg, this,
00461 wv::ref(*c), _1));
00462 c->add_callback(WvDBusConn::PriGaveUp,
00463 wv::bind(&WvDBusServer::do_gaveup_msg, this,
00464 wv::ref(*c), _1));
00465
00466 append(c, true, "wvdbus servconn");
00467 }