diff --git cmake/SimutransSourceList.cmake cmake/SimutransSourceList.cmake
index ba78610c0..315789d4f 100644
--- cmake/SimutransSourceList.cmake
+++ cmake/SimutransSourceList.cmake
@@ -171,6 +171,7 @@ target_sources(simutrans PRIVATE
 		src/simutrans/gui/pakinstaller.cc
 		src/simutrans/gui/pakselector.cc
 		src/simutrans/gui/password_frame.cc
+		src/simutrans/gui/permission_frame.cc
 		src/simutrans/gui/player_frame.cc
 		src/simutrans/gui/player_ranking_frame.cc
 		src/simutrans/gui/privatesign_info.cc
diff --git simutrans/history.txt simutrans/history.txt
index 2fa8191d8..254a7f4a6 100644
--- simutrans/history.txt
+++ simutrans/history.txt
@@ -12,6 +12,7 @@
 	CHG: (poppo) producing factories can also generate electricity (use with care)
 	ADD: Optional 'electricity_producer' flag (can be 0 or 1) for factories to override electricity producer status based on object name
 	ADD: (makie) more settings for inital climates in simuconf.tab
+   ADD: (nazalassa) permissions for halts, to let other players serve specific halts
 	FIX: (janry) admin packets only read or write for admin tool/server
 	FIX: drive again to the end of the platform after choose signal selection
 	FIX: pak specific config in user_dir/addons/PAK_NAM/config/simuconf.tab for reset extended settings
diff --git simutrans/text/en/station_details.txt simutrans/text/en/station_details.txt
index 5f674da1f..fd9e918e5 100644
--- simutrans/text/en/station_details.txt
+++ simutrans/text/en/station_details.txt
@@ -14,6 +14,9 @@ If all items are not visible <a href="mouse.txt">re-size</a> <strong>Stop Detail
 </p>
 <p>
 Information listed includes:<br>
+<em>Permissions:</em> opens the permission window, in which other players can be allowed to serve Stop.
+</p>
+<p>
 <em>Connected factories:</em> names and map co-ordinates (in brackets) of <a href="industry_info.txt">industry</a> in catchment area of Stop.<br>
 {Tips: Use
 <a href="keys.txt">[v]</a> to toggle view of catchment area of stops in game.}
diff --git src/simutrans/gui/halt_info.cc src/simutrans/gui/halt_info.cc
index 66c83aaed..0b0f1b7a5 100644
--- src/simutrans/gui/halt_info.cc
+++ src/simutrans/gui/halt_info.cc
@@ -6,6 +6,7 @@
 #include <algorithm>
 
 #include "halt_info.h"
+#include "permission_frame.h"
 #include "components/gui_button_to_chart.h"
 
 #include "../world/simworld.h"
@@ -174,8 +175,14 @@ private:
 public:
 	uint8 destination_counter; // last destination counter of the halt; if mismatch to current, then redraw destinations
 
+	/**
+	 * Pops up the "permissions" dialog. This is directly managed by the halt info frame. (So not sure it really fits here.)
+	 */
+	button_t permission_details;
+
 	gui_halt_detail_t(halthandle_t h) : gui_aligned_container_t()
 	{
+		permission_details.init(button_t::roundbox, "Permissions");
 		destination_counter = 0xFF;
 		cached_line_count = 0xFFFFFFFFul;
 		cached_convoy_count = 0xFFFFFFFFul;
@@ -292,7 +299,8 @@ void halt_info_t::init(halthandle_t halt)
 {
 	this->halt = halt;
 	set_name(halt->get_name());
-	set_owner(halt->get_owner() );
+	set_owner(halt->get_owner());
+	permission_frame = NULL;
 
 	set_table_layout(1,0);
 
@@ -388,6 +396,7 @@ void halt_info_t::init(halthandle_t halt)
 
 	// connection info
 	switch_mode.add_tab(&scrolly_details, translator::translate("Connections"));
+	halt_detail->permission_details.add_listener(this);
 	halt_detail->update_connections(halt);
 	halt_detail->set_size( halt_detail->get_min_size() );
 
@@ -432,6 +441,9 @@ halt_info_t::~halt_info_t()
 	}
 	delete departure_board;
 	delete halt_detail;
+	if (permission_frame) {
+		destroy_win(permission_frame);
+	}
 }
 
 
@@ -543,6 +555,7 @@ void gui_halt_detail_t::update_connections( halthandle_t halt )
 	slist_tpl<const goods_desc_t *> nimmt_an;
 
 	set_table_layout(2,0);
+	add_component(&permission_details, 2);
 	new_component_span<gui_label_t>("Fabrikanschluss", 2);
 
 	if (!fab_list.empty()) {
@@ -978,6 +991,15 @@ bool halt_info_t::action_triggered( gui_action_creator_t *comp, value_t )
 	else if (comp == &switch_mode) {
 		departure_board->next_refresh = -1;
 	}
+	else if (comp == &halt_detail->permission_details) {
+		if (permission_frame) {
+			top_win(permission_frame);
+		}
+		else {
+			permission_frame = new permission_frame_t(this, halt);
+			create_win(permission_frame, w_info, magic_none);
+		}
+	}
 
 	return true;
 }
diff --git src/simutrans/gui/halt_info.h src/simutrans/gui/halt_info.h
index a2a26e04c..b9390484f 100644
--- src/simutrans/gui/halt_info.h
+++ src/simutrans/gui/halt_info.h
@@ -81,6 +81,11 @@ private:
 
 	gui_tab_panel_t switch_mode;
 
+	/**
+	 * The permission frame for this halt, if any
+	 */
+	gui_frame_t *permission_frame;
+
 	halthandle_t halt;
 	char edit_name[256];
 
@@ -99,6 +104,11 @@ public:
 	 */
 	const char *get_help_filename() const OVERRIDE {return "station.txt";}
 
+	/**
+	 * The permission frame tells us when it is closed.
+	 */
+	void permission_frame_closed() { permission_frame = NULL; }
+
 	/**
 	 * Draw new component. The values to be passed refer to the window
 	 * i.e. It's the screen coordinates of the window where the
diff --git src/simutrans/gui/permission_frame.cc src/simutrans/gui/permission_frame.cc
new file mode 100644
index 000000000..39dc8ed6c
--- /dev/null
+++ src/simutrans/gui/permission_frame.cc
@@ -0,0 +1,88 @@
+/*
+ * This file is part of the Simutrans project under the Artistic License.
+ * (see LICENSE.txt)
+ */
+
+#include "permission_frame.h"
+
+#include "../world/simworld.h"
+#include "../tool/simmenu.h"
+#include "../player/simplay.h"
+
+#include "../dataobj/translator.h"
+
+
+permission_frame_t::permission_frame_t(halt_info_t *main_frame, halthandle_t halt) :
+	gui_frame_t( translator::translate("Set permissions"), halt->get_owner() )
+{
+	this->main_frame = main_frame;
+	this->halt = halt;
+
+	set_table_layout(1,0);
+
+	uint16 mask = halt->get_permissions();
+	for (int i=0;  i<PLAYER_UNOWNED;  i++) {
+		if (welt->get_player(i)) {
+			players[i].init(button_t::square_state, welt->get_player(i)->get_name());
+			players[i].add_listener(this);
+			players[i].pressed = (mask & (1<<i)) != 0;
+		} else {
+			players[i].init(button_t::square_state, "");
+			players[i].disable();
+		}
+		add_component(&players[i]);
+	}
+
+	reset_min_windowsize();
+	set_windowsize(get_min_windowsize());
+}
+
+
+permission_frame_t::~permission_frame_t()
+{
+	main_frame->permission_frame_closed();
+}
+
+
+/**
+ * Draw new component. The values to be passed refer to the window
+ * i.e. It's the screen coordinates of the window where the
+ * component is displayed.
+ */
+void permission_frame_t::draw(scr_coord pos, scr_size size)
+{
+	assert(halt.is_bound());
+
+	uint16 mask = halt->get_permissions();
+	for (int i=0;  i<PLAYER_UNOWNED;  i++) {
+		const bool new_state = (mask & (1<<i)) != 0;
+		if (players[i].pressed != new_state) {
+			players[i].pressed = new_state;
+			set_dirty();
+		}
+	}
+
+	gui_frame_t::draw(pos, size);
+}
+
+
+/**
+ * This method is called if an action is triggered
+ */
+bool permission_frame_t::action_triggered(gui_action_creator_t *comp, value_t /* */)
+{
+	for (int i=0;  i<PLAYER_UNOWNED;  i++) {
+		if (comp == &players[i]) {
+			uint16_t mask = halt->get_permissions();
+			mask ^= (1 << i);
+			char param[64];
+			sprintf(param, "%u,%u", halt.get_id(), mask);
+			tool_t *tool = create_tool( TOOL_SET_PERMISSION | SIMPLE_TOOL );
+			tool->set_default_param( param );
+			welt->set_tool( tool, welt->get_active_player() );
+			// since init always returns false, it is safe to delete immediately
+			delete tool;
+		}
+	}
+	return true;
+}
diff --git src/simutrans/gui/permission_frame.h src/simutrans/gui/permission_frame.h
new file mode 100644
index 000000000..1cf5d2101
--- /dev/null
+++ src/simutrans/gui/permission_frame.h
@@ -0,0 +1,31 @@
+/*
+ * This file is part of the Simutrans project under the Artistic License.
+ * (see LICENSE.txt)
+ */
+
+#ifndef GUI_PERMISSION_FRAME_H
+#define GUI_PERMISSION_FRAME_H
+
+
+#include "components/action_listener.h"
+#include "components/gui_button.h"
+#include "gui_frame.h"
+#include "halt_info.h"
+
+
+class permission_frame_t : public gui_frame_t, action_listener_t
+{
+	halt_info_t *main_frame;
+	halthandle_t halt;
+	button_t players[16];
+
+public:
+	permission_frame_t(halt_info_t *main_frame, halthandle_t halt);
+	~permission_frame_t();
+
+	void draw(scr_coord pos, scr_size size) OVERRIDE;
+
+	bool action_triggered(gui_action_creator_t*, value_t) OVERRIDE;
+};
+
+#endif
diff --git src/simutrans/gui/simwin.h src/simutrans/gui/simwin.h
index 3b579250b..15e5a4f54 100644
--- src/simutrans/gui/simwin.h
+++ src/simutrans/gui/simwin.h
@@ -120,7 +120,7 @@ enum magic_numbers {
 	magic_haltlist_filter,
 	magic_depot, // only used to load/save
 	magic_depotlist   = magic_depot + MAX_PLAYER_COUNT,
-	magic_vehiclelist = magic_depotlist   + MAX_PLAYER_COUNT,
+	magic_vehiclelist = magic_depotlist + MAX_PLAYER_COUNT,
 	magic_pakinstall,
 	magic_chatframe,
 	magic_player_ranking,
diff --git src/simutrans/simhalt.cc src/simutrans/simhalt.cc
index 170690fd2..41bdde604 100644
--- src/simutrans/simhalt.cc
+++ src/simutrans/simhalt.cc
@@ -194,13 +194,13 @@ halthandle_t haltestelle_t::get_halt_koord_index(koord k)
 
 
 /* we allow only for a single stop per grund
- * this will only return something if this stop belongs to same player or is public, or is a dock (when on water)
+ * this will only return something if this stop belongs to same player or is public or its permissions allow access, or is a dock (when on water)
  */
 halthandle_t haltestelle_t::get_halt(const koord3d pos, const player_t *player )
 {
 	const grund_t *gr = welt->lookup(pos);
 	if(gr) {
-		if(gr->get_halt().is_bound()  &&  player_t::check_owner(player,gr->get_halt()->get_owner())  ) {
+		if(gr->get_halt().is_bound()  &&  gr->get_halt()->can_player_stop(player)) {
 			return gr->get_halt();
 		}
 		// no halt? => we do the water check
@@ -218,7 +218,7 @@ halthandle_t haltestelle_t::get_halt(const koord3d pos, const player_t *player )
 			// then for public stop
 			for(  uint8 i=0;  i<cnt;  i++  ) {
 				halthandle_t halt = plan->get_haltlist()[i];
-				if(  halt->get_owner()==welt->get_public_player()  &&  halt->get_station_type()&dock  ) {
+				if(  (halt->get_owner()==welt->get_public_player()  ||  (halt->get_permissions() & (1 << player->get_player_nr())))  &&  halt->get_station_type()&dock  ) {
 					return halt;
 				}
 			}
@@ -519,6 +519,7 @@ haltestelle_t::haltestelle_t(koord k, player_t* player)
 	last_bar_count = 0;
 
 	init_financial_history();
+	set_permissions(0);
 }
 
 
@@ -617,6 +618,19 @@ void haltestelle_t::rotate90( const sint16 y_size )
 }
 
 
+void haltestelle_t::set_permissions(uint16 perms) {
+	// Owner and public service are always allowed here
+	// If public, everyone is allowed here
+	permissions = owner->is_public_service() ? 0xFFFF : (perms | (1<<owner->get_player_nr()) | (1<<welt->get_public_player()->get_player_nr()));
+}
+
+
+bool haltestelle_t::can_player_stop(const player_t *player) const
+{
+	return player_t::check_owner(player, owner) || (permissions&(1<<player->get_player_nr()));
+}
+
+
 
 const char* haltestelle_t::get_name() const
 {
@@ -2566,6 +2580,9 @@ void haltestelle_t::change_owner( player_t *player )
 
 	// now finally change owner
 	owner = player;
+	// Old player is still allowed to serve this stop - FIXME?
+	// Should not matter as stops can only be made public (for now)
+	set_permissions(permissions);
 	rebuild_connections();
 	rebuild_linked_connections();
 	rebuild_connected_components();
@@ -2625,6 +2642,9 @@ void haltestelle_t::merge_halt( halthandle_t halt_merged )
 	halt_merged->transfer_goods(self);
 	destroy(halt_merged);
 
+	// Allow everyone who could serve either halt to continue serving it
+	set_permissions(halt_merged->get_permissions() | permissions);
+
 	recalc_basis_pos();
 
 	// also rebuild our connections
@@ -2961,6 +2981,14 @@ void haltestelle_t::rdwr(loadsave_t *file)
 			financial_history[k][HALT_WALKED] = 0;
 		}
 	}
+	if(  file->is_version_atleast(124, 5)  ) {
+		file->rdwr_short(permissions);
+	} else {
+		permissions = 0;
+	}
+	if (file->is_loading()) {
+		set_permissions(permissions);
+	}
 }
 
 
diff --git src/simutrans/simhalt.h src/simutrans/simhalt.h
index 0e434ad80..63a26dbd6 100644
--- src/simutrans/simhalt.h
+++ src/simutrans/simhalt.h
@@ -313,6 +313,11 @@ private:
 	player_t *owner;
 	static karte_ptr_t welt;
 
+	/**
+	 * What players are allowed to stop here
+	 */
+	uint16 permissions;
+
 	/**
 	 * What is that for a station (for the image)
 	 */
@@ -700,6 +705,19 @@ public:
 
 	void set_name(const char *name);
 
+	uint16 get_permissions() const { return permissions; }
+
+	/**
+	 * @return whether a player is allowed to stop here
+	 */
+	bool can_player_stop(const player_t *player) const;
+
+	/**
+	 * Sets permissions
+	 * Owner and public service are always allowed
+	 */
+	void set_permissions(uint16 perms);
+
 	// create an unique name: better to be called with valid handle, although it will work without
 	char* create_name(koord k, char const* typ);
 
diff --git src/simutrans/tool/simmenu.cc src/simutrans/tool/simmenu.cc
index c9938eb4f..7061f77cf 100644
--- src/simutrans/tool/simmenu.cc
+++ src/simutrans/tool/simmenu.cc
@@ -157,6 +157,7 @@ const char* tool_t::id_to_string(uint16 id)
 			CASE_TO_STRING(TOOL_CHANGE_TRAFFIC_LIGHT);
 			CASE_TO_STRING(TOOL_CHANGE_CITY);
 			CASE_TO_STRING(TOOL_RENAME);
+			CASE_TO_STRING(TOOL_SET_PERMISSION);
 			CASE_TO_STRING(TOOL_TOGGLE_RESERVATION);
 			CASE_TO_STRING(TOOL_VIEW_OWNER);
 			CASE_TO_STRING(TOOL_HIDE_UNDER_CURSOR);
@@ -340,6 +341,7 @@ tool_t* create_simple_tool(int toolnr)
 	case TOOL_LOAD_SCENARIO:        tool = new tool_load_scenario_t();        break;
 	case TOOL_DAY_NIGHT_TOGGLE:     tool = new tool_day_night_toggle_t();     break;
 	case TOOL_SINGLE_WAY_TOOGLE:    tool = new tool_show_single_ways_t();     break;
+	case TOOL_SET_PERMISSION:       tool = new tool_change_permission_t();     break;
 	case UNUSED_TOOL_ADD_MESSAGE: // fall-through - intended!!!111elf
 	case UNUSED_WKZ_PWDHASH_TOOL:
 		dbg->warning("create_simple_tool()", "Deprecated tool [%i] requested", toolnr);
@@ -1279,7 +1281,7 @@ void toolbar_last_used_t::clear()
 // currently only needed for last used tools
 void toolbar_last_used_t::append(tool_t* t, player_t* sp)
 {
-	static int exclude_from_adding[8] = {
+	static int exclude_from_adding[9] = {
 		TOOL_SCHEDULE_ADD | GENERAL_TOOL,
 		TOOL_SCHEDULE_INS | GENERAL_TOOL,
 		TOOL_CHANGE_CONVOI | SIMPLE_TOOL,
@@ -1287,7 +1289,8 @@ void toolbar_last_used_t::append(tool_t* t, player_t* sp)
 		TOOL_CHANGE_DEPOT | SIMPLE_TOOL,
 		UNUSED_WKZ_PWDHASH_TOOL | SIMPLE_TOOL,
 		TOOL_CHANGE_PLAYER | SIMPLE_TOOL,
-		TOOL_RENAME | SIMPLE_TOOL
+		TOOL_RENAME | SIMPLE_TOOL,
+		TOOL_SET_PERMISSION | SIMPLE_TOOL
 	};
 
 	if (!sp || t->get_icon(sp) == IMG_EMPTY) {
diff --git src/simutrans/tool/simmenu.h src/simutrans/tool/simmenu.h
index 7522550d7..c1adaaa1c 100644
--- src/simutrans/tool/simmenu.h
+++ src/simutrans/tool/simmenu.h
@@ -133,6 +133,7 @@ enum {
 	TOOL_DAY_NIGHT_TOGGLE,
 	TOOL_SINGLE_WAY_TOOGLE,
 	TOOL_WORK_MAP,
+	TOOL_SET_PERMISSION,
 	SIMPLE_TOOL_COUNT,
 	SIMPLE_TOOL = 0x2000
 };
diff --git src/simutrans/tool/simtool.cc src/simutrans/tool/simtool.cc
index 7e311a8c2..aa744d944 100644
--- src/simutrans/tool/simtool.cc
+++ src/simutrans/tool/simtool.cc
@@ -2504,6 +2504,7 @@ const char *tool_plant_groundobj_t::work( player_t *player, koord3d pos )
  * the following routines add waypoints/halts to a schedule
  * because we do not like to stop at AIs stop, but we still want to force the truck to use AI roads
  * So if there is a halt, then it must be either public or ours!
+ * (Except if permission has been explicitely granted.)
  */
 static const char *tool_schedule_insert_aux(karte_t *welt, player_t *player, koord3d pos, schedule_t *schedule, bool append)
 {
@@ -2535,7 +2536,7 @@ static const char *tool_schedule_insert_aux(karte_t *welt, player_t *player, koo
 				return "Das Feld gehoert\neinem anderen Spieler\n";
 			}
 		}
-		if(  bd->is_halt()  &&  !player_t::check_owner( player, bd->get_halt()->get_owner()) ) {
+		if(  bd->is_halt()  &&  !bd->get_halt()->can_player_stop(player)) {
 			return "Das Feld gehoert\neinem anderen Spieler\n";
 		}
 		// ok, now we have a valid ground
@@ -8702,6 +8703,23 @@ bool tool_rename_t::init(player_t *player)
 	return false;
 }
 
+bool tool_change_permission_t::init(player_t *player)
+{
+	uint16 id = 0, perms = 0;
+	const char *p = default_param;
+
+	id = atoi(p);
+	while(  *p>0  &&  *p++!=','  );
+	perms = atoi(p);
+
+	halthandle_t halt;
+	halt.set_id(id);
+	if(  halt.is_bound()  &&  player_t::check_owner(halt->get_owner(), player)  ) {
+		halt->set_permissions(perms);
+	}
+	return false;
+}
+
 bool tool_recolour_t::init(player_t *)
 {
 	// skip the rest of the command
diff --git src/simutrans/tool/simtool.h src/simutrans/tool/simtool.h
index fed73cfcd..a22f900eb 100644
--- src/simutrans/tool/simtool.h
+++ src/simutrans/tool/simtool.h
@@ -1313,7 +1313,7 @@ public:
 	// work is not safe, has to be send over network
 };
 
-// internal tool: rename stuff
+// internal tool: set owner
 class tool_change_owner_t : public tool_t {
 public:
 	tool_change_owner_t() : tool_t(TOOL_SET_OWNER | GENERAL_TOOL) {}
@@ -1322,4 +1322,12 @@ public:
 	// work is not safe, has to be send over network
 };
 
+// internal tool: set permissions
+class tool_change_permission_t : public tool_t {
+public:
+	tool_change_permission_t() : tool_t(TOOL_SET_PERMISSION | SIMPLE_TOOL) {}
+	bool init(player_t*) OVERRIDE;
+	bool is_init_keeps_game_state() const OVERRIDE { return false; }
+};
+
 #endif
