diff --git a/README.md b/README.md index ae8f591..08c96fd 100644 --- a/README.md +++ b/README.md @@ -1,166 +1,41 @@ -# pCloud Console Client (undead) +# Introduction -`pcloudcc` is simple **linux** console client for pCloud cloud storage derived from the console-client developed by pCloud. This version is independently maintained by me, whose only affiliation with pCloud is as a user of their services. Due credit goes to Anton Titov, Ivan Stoev, and pCloud. +`pcloudcc` is simple linux console client for pCloud cloud storage derived from the console-client developed by pCloud. This version is independently maintained by me, whose only affiliation with pCloud is as a user of their services. Due credit goes to Anton Titov, Ivan Stoev, and pCloud. -## mbedtls 3.x Migration Notice +# Supported Platforms & Distributions -`pcloudcc` now uses `mbedtls` version 3.x. This may already be included in your distribution, and if it is, you can ignore this section. If you're unlucky enough that your distribution still ships with `mbedtls` 2.x *(looking at you, Debian...)*, then try the following instructions. This has been tested on debian bookworm, **but you may have to adjust for your own distribution -- the command sequence below uses `apt` to install known build dependencies.** - -Without further ado, the first step is to build and install the `mbedtls` 3.x library on your host machine, hopefully without breaking the distribution version. Review the following commands, then copy and paste them into a terminal to run them. - -``` -sudo apt install python3 python3-pip python3-venv -mkdir -p $HOME/src; cd $HOME/src -git clone https://github.com/Mbed-TLS/mbedtls/ -cd mbedtls -git checkout tags/v3.6.2 -git submodule update --init -python3 -m venv ./venv -source ./venv/bin/activate -python3 -m pip install -r scripts/basic.requirements.txt -make -sudo make install -sudo ln -s /usr/local/include/mbedtls/ /usr/local/include/mbedtls3 -``` - -The symbolic link at the end resolves the ambiguity between `/usr/include/mbedtls` and `/usr/local/include/mbedtls`. Now, we need to make some edits to the Makefile and the source files to ensure that the build uses the correct `mbedtls` headers and libraries. We'll do this with `sed`, then run `make` as usual. - -``` -# run from the source root directory (e.g., pcloudcc-lneely) - -sed -i 's/-lmbedtls/-l:libmbedtls.a/;s/-lmbedcrypto/-l:libmbedcrypto.a/;s/-lmbedx509/-l:libmbedx509.a/' Makefile -sed -i '5s/$/ -I\/usr\/local\/include/' Makefile -sed -i '10s/$/ -L\/usr\/local\/lib\//' Makefile -find . -type f -name "*.[ch]" -exec sed -i 's/#include ` to enable a SOCKS proxy on `localhost:`. Configure your local web browser to use `localhost:` as its proxy, then log in to pcloud.com and validate the device. *Do not forget to remove the proxy from your browser configuration when done.* - -## Supported Distributions & Packages +The target operating system and platform for this program is `linux/amd64`, and has been reported to work on `linux/aarch64`. Program behavior on 32-bit architectures is [considered undefined](https://github.com/lneely/pcloudcc-lneely/issues/85). I aim to support as many distributions as possible, and maintain an [AUR](https://aur.archlinux.org/packages/pcloudcc-lneely) package. I do not plan on providing or maintaining any other packages, but encourage anyone interested in doing so for their own distributions. -## Dependencies -- zlib (-lz) -- boost (-lboost_system, -lboost_program_options) -- pthread (lpthread) -- udev (-ludev) -- fuse (-lfuse) -- sqlite (-lsqlite3) -- mbedtls (-lmbedtls -lmbedcrypto -lmbedx509) -- readline (-lreadline) +# Notices -## Building +## Security Notice -``` -make -``` +My code audits are best-effort, and I make security enhancements as I identify problems. However, I *do not* promise that this program is free of even older and well-known security vulnerabilities. Therefore, I recommend using this program only on a *trusted single-user* system. **Security-related code audits and contributions are most welcome!** -### Build Options +## Pending Transfers -``` -make BUILD=debug # include debug symbols, ASan instrumentation. (default: release) -make STATIC=0 # dynamically link libpcloudcc_lib.so. (default: 1) -make SCAN=1 # run code analysis; recommend CC=clang and CXX=clang++. (default: 0) -make CC=clang CXX=clang++ # use clang instead of gcc (default: gcc,g++) -make DESTDIR=/prefix # installation prefix (default: /usr/local/bin) -``` - -## Usage - -### Getting Help - -Terminal command is pcloudcc and -h option prints short options description. - -> pcloudcc -h - -### First Use - -Start the service in the foreground using the -p switch to enter your -password, and the -s switch to save the password to the -database. Verify that file system starts and mounts normally after the - -> pcloudcc -u example@myemail.com -p -s - -Optionally specify your own mount point. - -> pcloudcc -u example@myemail.com -p -s -m /path/to/mountpoint - -### Registration (UNTESTED) - -If you don't have existing user use -n switch to register new user: - -> pcloudcc -u example@myemail.com -p -s -n - -Notice that a new user may take a while to mount. Please, be patient. - -### Run as Daemon - -If you have saved your password, then you can run pcloudcc as a -background daemon. Verify the filesystem is mounted when the daemon -starts. - -> pcloudcc -u example@myemail.com -d - -### Command Prompt - -Use the command prompt to interact with a running daemon. - -> pcloudcc -k - -Command Reference: - -``` - help(?): Show this help message - crypto(c): - start : Unlock crypto folder - stop: Lock crypto folder - sync(s): - list(ls): List sync folders - add : Add sync folder - remove(rm) : Remove sync folder - finalize(f): Kill daemon and quit - quit(q): Exit this program -``` - -**Note**. Command line arguments that include special characters (e.g., the - `crypto start` password or paths with spaces) must be quoted or - escaped. In other words, instead of: - - `startcrypto Str0ng p4$$word 4 great jUSTicE!` - - One must now type, for example: - - `c start 'Str0ng p4$$word 4 great jUSTicE!'` - - If the password includes quotes for some reason, then those characters must - be escaped properly. - -## Warning - -**Stopping daemon will break pending background transfers!** +Stopping the daemon will break pending background transfers! `pcloudcc` does not currently provide a command to check for pending transfers. You can currently check this by ensuring there is only one file named `cached` in `~/.pcloud/Cache`. Usually this is a large file. +# See Also + +- [Contributing](./doc/CONTRIBUTING.md): Read this if you're interested in + helping out. **Short version**: do your best to write good code, be + courteous to maintainers, and most importantly: have fun! + +- [Building](./doc/BUILD.md): Read this if you're not sure how to build + `pcloudcc`. For most users, `make` and `make install` are enough. + +- [mbedtls 3.x migration notes](./doc/MBEDTLS-3.x.md): Read this if you use an + older distribution such as `debian:bookworm` and encounter build problems. + +- [Device Validation Guide](./doc/DEVICE-VALIDATION.md): Read this if you're + running `pcloudcc` and encounter device validation-related errors. + +- [Usage Guide](./doc/USAGE.md): Read this if you're unsure how to start using + `pcloudcc`. \ No newline at end of file diff --git a/doc/BUILD.md b/doc/BUILD.md new file mode 100644 index 0000000..b235a97 --- /dev/null +++ b/doc/BUILD.md @@ -0,0 +1,35 @@ +# Dependencies +- zlib (-lz) +- boost (-lboost_system, -lboost_program_options) +- pthread (lpthread) +- udev (-ludev) +- fuse (-lfuse) +- sqlite (-lsqlite3) +- mbedtls (-lmbedtls -lmbedcrypto -lmbedx509) +- readline (-lreadline) + +# Building +``` +make +``` + +(norly! 🦉) + +# Installing +``` +sudo make install +``` + +Specify `DESTDIR` to install to a prefix other than `/usr/local` (see [[#Build Options]]). + +# Build Options + +Use the following options to influence the build process. + +``` +make BUILD=debug # include debug symbols, ASan instrumentation. (default: release) +make STATIC=0 # dynamically link libpcloudcc_lib.so. (default: 1) +make SCAN=1 # run code analysis; recommend CC=clang and CXX=clang++. (default: 0) +make CC=clang CXX=clang++ # use clang instead of gcc (default: gcc,g++) +make DESTDIR=/prefix # installation prefix (default: /usr/local/bin) +``` diff --git a/doc/CONTRIBUTING.md b/doc/CONTRIBUTING.md new file mode 100644 index 0000000..ca1b90d --- /dev/null +++ b/doc/CONTRIBUTING.md @@ -0,0 +1,31 @@ +# Contributing to pcloudcc + +Contributions are welcome and appreciated, and this guide is intentionally short and open-ended. This document assumes you are familiar with GitHub contribution mechanisms. If you need help or clarification, feel free to ask. + +# Coding Guide + +Contributors should aim to produce robust, maintainable software that is +enjoyable to work with. In service of this goal, consider the following +guidelines: + +1. Choose simple solutions over clever ones. + +2. Choose readability over a smaller line count. + +3. Choose short names that convey meaning in context. + +4. Imitate the coding style in the file you're working on, and use your +judgment. + +5. Specification comments are helpful in headers, but should be a last resort +in implementation (see points 1 and 2). + +6. Please be courteous to maintainers by making changes as small as necessary, +and by maintaining a clean and linear commit history. + +Look to the `pshm` library to illustrate (however imperfectly) how I am +applying these principles. + +# Code of Conduct + +Building software should be enjoyable and fun for *all* involved. Make it so 🖖. diff --git a/doc/DEVICE-VALIDATION.md b/doc/DEVICE-VALIDATION.md new file mode 100644 index 0000000..d8cf351 --- /dev/null +++ b/doc/DEVICE-VALIDATION.md @@ -0,0 +1,15 @@ +# Validating Your Device + +pCloud requires "first time" validation for all devices. In the standard use case, where the user is running `pcloudcc` on the same host s/he normally works on, this can be completed by simply logging into pcloud.com using the web browser (and often, it already has been). Non-standard use cases (e.g., running `pcloudcc` on a remote server) require a different approach. + +# Workarounds + +Props to [@tieum](https://github.com/tieum), [@ebouda333](https://github.com/ebouda33), [@CorvusCorax](https://github.com/CorvusCorax), and [@tomash](https://github.com/tomash) for suggesting the following workarounds: + +**Dockerized Carbonyl**. *Requires Docker or Podman on the host.*. Run [carbonyl](https://github.com/fathyb/carbonyl) in a container on the target host to complete the validation. + +``` +docker run --network host --rm -ti fathyb/carbonyl https://my.pcloud.com +``` + +**SOCKS proxy over SSH** *Requires TCP port forwarding over SSH*. Log in to the remote host using the command `ssh -D ` to enable a SOCKS proxy on `localhost:`. Configure your local web browser to use `localhost:` as its proxy, then log in to pcloud.com and validate the device. *Do not forget to remove the proxy from your browser configuration when done.* diff --git a/doc/MBEDTLS-3.x.md b/doc/MBEDTLS-3.x.md new file mode 100644 index 0000000..66eaff5 --- /dev/null +++ b/doc/MBEDTLS-3.x.md @@ -0,0 +1,34 @@ +## mbedtls 3.x Migration Notice + +`pcloudcc` now uses `mbedtls` version 3.x. This may already be included in your distribution, and if it is, you can ignore this section. If you're unlucky enough that your distribution still ships with `mbedtls` 2.x *(looking at you, Debian...)*, then try the following instructions. This has been tested on debian bookworm, **but you may have to adjust for your own distribution -- the command sequence below uses `apt` to install known build dependencies.** + +Without further ado, the first step is to build and install the `mbedtls` 3.x library on your host machine, hopefully without breaking the distribution version. Review the following commands, then copy and paste them into a terminal to run them. + +``` +sudo apt install python3 python3-pip python3-venv +mkdir -p $HOME/src; cd $HOME/src +git clone https://github.com/Mbed-TLS/mbedtls/ +cd mbedtls +git checkout tags/v3.6.2 +git submodule update --init +python3 -m venv ./venv +source ./venv/bin/activate +python3 -m pip install -r scripts/basic.requirements.txt +make +sudo make install +sudo ln -s /usr/local/include/mbedtls/ /usr/local/include/mbedtls3 +``` + +The symbolic link at the end resolves the ambiguity between `/usr/include/mbedtls` and `/usr/local/include/mbedtls`. Now, we need to make some edits to the Makefile and the source files to ensure that the build uses the correct `mbedtls` headers and libraries. We'll do this with `sed`, then run `make` as usual. + +``` +# run from the source root directory (e.g., pcloudcc-lneely) + +sed -i 's/-lmbedtls/-l:libmbedtls.a/;s/-lmbedcrypto/-l:libmbedcrypto.a/;s/-lmbedx509/-l:libmbedx509.a/' Makefile +sed -i '5s/$/ -I\/usr\/local\/include/' Makefile +sed -i '10s/$/ -L\/usr\/local\/lib\//' Makefile +find . -type f -name "*.[ch]" -exec sed -i 's/#include pcloudcc -h + +## First Use + +Start the service in the foreground using the -p switch to enter your +password, and the -s switch to save the password to the +database. Verify that file system starts and mounts normally after the + +> pcloudcc -u example@myemail.com -p -s + +Optionally specify your own mount point. + +> pcloudcc -u example@myemail.com -p -s -m /path/to/mountpoint + +## Registration (UNTESTED) + +If you don't have existing user use -n switch to register new user: + +> pcloudcc -u example@myemail.com -p -s -n + +Notice that a new user may take a while to mount. Please, be patient. + +## Run as Daemon + +If you have saved your password, then you can run pcloudcc as a +background daemon. Verify the filesystem is mounted when the daemon +starts. + +> pcloudcc -u example@myemail.com -d + +## Command Prompt + +Use the command prompt to interact with a running daemon. + +> pcloudcc -k + +Command Reference: + +``` + help(?): Show this help message + crypto(c): + start : Unlock crypto folder + stop: Lock crypto folder + sync(s): + list(ls): List sync folders + add : Add sync folder + remove(rm) : Remove sync folder + finalize(f): Kill daemon and quit + quit(q): Exit this program +``` + +**Note**. Command line arguments that include special characters (e.g., the + `crypto start` password or paths with spaces) must be quoted or + escaped. In other words, instead of: + + `startcrypto Str0ng p4$$word 4 great jUSTicE!` + + One must now type, for example: + + `c start 'Str0ng p4$$word 4 great jUSTicE!'` + + If the password includes quotes for some reason, then those characters must + be escaped properly. diff --git a/doc/img/request-response-00.png b/doc/img/request-response-00.png deleted file mode 100644 index 560d0a9..0000000 Binary files a/doc/img/request-response-00.png and /dev/null differ diff --git a/doc/request-response.org b/doc/request-response.org deleted file mode 100644 index 8967e0b..0000000 --- a/doc/request-response.org +++ /dev/null @@ -1,36 +0,0 @@ -#+title: pCloudCC Request-Response Flow - -~pcloudcc~ is built upon a client-server -architecture. When the user runs ~pcloudcc -u ~, this starts -the server. The server listens for request messages and sends back -response messages. - -When the user runs ~pcloudcc -k~, this starts the client's command -line interface. The client sends request messages to and expects a -corresponding response message from the server. - -The client and server send and receive messages over a Unix socket -that gets created when the server is started. - -#+begin_src plantuml :file img/request-response-00.png :export results - participant user - participant control_tools <> - participant overlay_client <> - participant socket <> - participant poverlay <> - - user -> control_tools: input command - control_tools -> control_tools : parse command - control_tools -> overlay_client : SendCall(msgType, reqData, ...) - overlay_client -> socket : write_request - socket -> poverlay : request - poverlay -> poverlay : poverlay_handle_request - poverlay -> poverlay : poverlay_get_response - poverlay -> socket : response - socket -> overlay_client : read_response - overlay_client -> control_tools : (msg, msgsz, payload, payloadsz) - control_tools -> user : print results -#+end_src - -[[file:img/request-response-00.png]] - diff --git a/pclsync/plibs.c b/pclsync/plibs.c index c2865d1..e5d8988 100644 --- a/pclsync/plibs.c +++ b/pclsync/plibs.c @@ -34,6 +34,7 @@ #include "pdatabase.h" #include "plocks.h" #include "pnetlibs.h" +#include "ppath.h" #include "prun.h" #include "psettings.h" #include "ptimer.h" @@ -2575,6 +2576,23 @@ static void time_format(time_t tm, unsigned long ns, char *result) { memcpy(result, " +0000", 7); // copies the null byte } +char *psync_debug_path() { + char *home = ppath_home(); + if (!home) { + return NULL; + } + + const char *subdir = "/.pcloud/debug.log"; + size_t len = strlen(home) + strlen(subdir) + 1; + char *sockpath = (char *)malloc(len); + if (!sockpath) { + return NULL; + } + + snprintf(sockpath, len, "%s%s", home, subdir); + return sockpath; +} + int psync_debug(const char *file, const char *function, int unsigned line, int unsigned level, const char *fmt, ...) { if (!IS_DEBUG) @@ -2599,7 +2617,9 @@ int psync_debug(const char *file, const char *function, int unsigned line, break; } if (unlikely(!log)) { - log = fopen(DEBUG_FILE, "a+"); + char *path = psync_debug_path(); + log = fopen(path, "a+"); + free(path); if (!log) return 1; } diff --git a/pclsync/plibs.h b/pclsync/plibs.h index 835ec9a..e40f3b5 100644 --- a/pclsync/plibs.h +++ b/pclsync/plibs.h @@ -71,8 +71,6 @@ extern "C" { #define IS_DEBUG (DEBUG_LEVEL >= D_WARNING) -#define DEBUG_FILE "/tmp/psync_err.log" - #if defined(assert) #undef assert #endif diff --git a/pclsync/prpc.c b/pclsync/prpc.c index 963a8cd..12a0f88 100644 --- a/pclsync/prpc.c +++ b/pclsync/prpc.c @@ -40,11 +40,13 @@ #include #include #include +#include #include #include #include #include "prpc.h" +#include "ppath.h" #include "ppathstatus.h" #include "plibs.h" @@ -228,22 +230,29 @@ void prpc_main_loop() { struct sockaddr_un addr; int fd, cl; + char *sockpath = prpc_sockpath(); if ((fd = socket(AF_UNIX, SOCK_STREAM, 0)) == -1) { - debug(D_ERROR, "Unix socket error failed to open %s", PRPC_SOCK_PATH); + debug(D_ERROR, "Unix socket error failed to open %s", sockpath); + return; + } + + if (fchmod(fd, 0600) == -1) { + debug(D_ERROR, "Failed to set socket permissions"); return; } memset(&addr, 0, sizeof(addr)); addr.sun_family = AF_UNIX; - strncpy(addr.sun_path, PRPC_SOCK_PATH, sizeof(addr.sun_path) - 1); + strncpy(addr.sun_path, sockpath, sizeof(addr.sun_path) - 1); - unlink(PRPC_SOCK_PATH); + unlink(sockpath); - if (bind(fd, (struct sockaddr *)&addr, - strlen(PRPC_SOCK_PATH) + sizeof(addr.sun_family)) == -1) { + if (bind(fd, (struct sockaddr *)&addr, strlen(sockpath) + sizeof(addr.sun_family)) == -1) { debug(D_ERROR, "Unix socket bind error"); return; } + + free(sockpath); if (listen(fd, 5) == -1) { debug(D_ERROR, "Unix socket listen error"); @@ -286,3 +295,20 @@ void prpc_init() { handlers = (prpc_handler *)psync_malloc(sizeof(prpc_handler) * handlers_size); memset(handlers, 0, sizeof(prpc_handler) * handlers_size); } + +char *prpc_sockpath() { + char *home = ppath_home(); + if (!home) { + return NULL; + } + + const char *subdir = "/.pcloud/prpc.sock"; + size_t len = strlen(home) + strlen(subdir) + 1; + char *sockpath = (char *)malloc(len); + if (!sockpath) { + return NULL; + } + + snprintf(sockpath, len, "%s%s", home, subdir); + return sockpath; +} diff --git a/pclsync/prpc.h b/pclsync/prpc.h index ceb917c..e7bb076 100644 --- a/pclsync/prpc.h +++ b/pclsync/prpc.h @@ -39,10 +39,6 @@ extern "C" { #include -#ifndef PRPC_SOCK_PATH -#define PRPC_SOCK_PATH "/tmp/pcloud_unix_soc.sock" -#endif - typedef struct _rpc_message_t { uint32_t type; uint64_t length; @@ -69,6 +65,7 @@ typedef int (*prpc_handler)(const char *); void prpc_main_loop(void); void prpc_init(); int prpc_register(int cmdid, prpc_handler h); +char *prpc_sockpath(); #ifdef __cplusplus } diff --git a/pclsync/pshm.c b/pclsync/pshm.c index 6eeb71f..9b8e64e 100644 --- a/pclsync/pshm.c +++ b/pclsync/pshm.c @@ -11,7 +11,7 @@ #include "ppath.h" #include "plibs.h" -key_t pshm_get_key() { +static key_t get_key() { char path[PATH_MAX]; char *home; @@ -25,10 +25,10 @@ key_t pshm_get_key() { return ftok(path, 'A'); } -int pshm_getid() { +static int get_id() { key_t key; - key = pshm_get_key(); + key = get_key(); if(key == -1) { debug(D_ERROR, "failed to get ipc key"); return -1; @@ -43,7 +43,7 @@ bool pshm_read(void **data, size_t *datasz) { int flag; char *dataArea; - shmid = pshm_getid(); + shmid = get_id(); if (shmid == -1) { debug(D_ERROR, "Failed to get shared memory ID"); return false; @@ -91,7 +91,7 @@ void pshm_write(const void *data, size_t datasz) { return; } - shmid = pshm_getid(); + shmid = get_id(); if (shmid == -1) { debug(D_ERROR, "Failed to get shared memory ID"); return; @@ -125,5 +125,5 @@ void pshm_write(const void *data, size_t datasz) { } int pshm_cleanup() { - return shmctl(pshm_getid(), IPC_RMID, NULL); + return shmctl(get_id(), IPC_RMID, NULL); } \ No newline at end of file diff --git a/rpcclient.cpp b/rpcclient.cpp index 5641db4..c6991bf 100644 --- a/rpcclient.cpp +++ b/rpcclient.cpp @@ -24,9 +24,7 @@ #define POVERLAY_READ_INCOMPLETE -105 #define POVERLAY_READ_INVALID_RESPONSE -106 -RpcClient::RpcClient() { - this->sockpath = "/tmp/pcloud_unix_soc.sock"; -} +RpcClient::RpcClient() {} RpcClient::~RpcClient() {} @@ -163,7 +161,9 @@ int RpcClient::Call(int id, const char *path, char **errm, size_t *errmsz) { int result = 0; int sockfd = -1; - sockfd = this->connectSocket(PRPC_SOCK_PATH, errm, errmsz); + char *sockpath = prpc_sockpath(); + sockfd = this->connectSocket(sockpath, errm, errmsz); + free(sockpath); if (sockfd >= 0) { if ((result = this->writeRequest(sockfd, id, path, errm, errmsz)) == 0) { result = this->readResponse(sockfd, errm, errmsz); diff --git a/rpcclient.h b/rpcclient.h index be42ccc..4c11f23 100644 --- a/rpcclient.h +++ b/rpcclient.h @@ -13,8 +13,6 @@ typedef enum _pCloud_FileState { class RpcClient { private: - const char *sockpath; - int connectSocket(const char *sockpath, char **out, size_t *out_size); int writeRequest(int fd, int msgtype, const char *value, char **out, size_t *out_size); int readResponse(int fd, char **out, size_t *out_size);